diff --git a/source/build-aux/config.guess b/source/build-aux/config.guess
index 7f76b6228f73d674f58cfcc3523f99e253ee5515..1817bdce90dc4d6263ba3637e5880c25e363588f 100755
--- a/source/build-aux/config.guess
+++ b/source/build-aux/config.guess
@@ -4,7 +4,7 @@
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2022-01-09'
+timestamp='2022-05-25'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -1151,16 +1151,27 @@ EOF
 	;;
     x86_64:Linux:*:*)
 	set_cc_for_build
+	CPU=$UNAME_MACHINE
 	LIBCABI=$LIBC
 	if test "$CC_FOR_BUILD" != no_compiler_found; then
-	    if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
-		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
-		grep IS_X32 >/dev/null
-	    then
-		LIBCABI=${LIBC}x32
-	    fi
+	    ABI=64
+	    sed 's/^	    //' << EOF > "$dummy.c"
+	    #ifdef __i386__
+	    ABI=x86
+	    #else
+	    #ifdef __ILP32__
+	    ABI=x32
+	    #endif
+	    #endif
+EOF
+	    cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+	    eval "$cc_set_abi"
+	    case $ABI in
+		x86) CPU=i686 ;;
+		x32) LIBCABI=${LIBC}x32 ;;
+	    esac
 	fi
-	GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
+	GUESS=$CPU-pc-linux-$LIBCABI
 	;;
     xtensa*:Linux:*:*)
 	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
@@ -1367,8 +1378,11 @@ EOF
     BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
 	GUESS=i586-pc-haiku
 	;;
-    x86_64:Haiku:*:*)
-	GUESS=x86_64-unknown-haiku
+    ppc:Haiku:*:*)	# Haiku running on Apple PowerPC
+	GUESS=powerpc-apple-haiku
+	;;
+    *:Haiku:*:*)	# Haiku modern gcc (not bound by BeOS compat)
+	GUESS=$UNAME_MACHINE-unknown-haiku
 	;;
     SX-4:SUPER-UX:*:*)
 	GUESS=sx4-nec-superux$UNAME_RELEASE
diff --git a/source/build-aux/config.sub b/source/build-aux/config.sub
index 9b62e37c43c6d67efb7a7de7ee1f19e0bc9bc311..dba16e84c77c7d25871d80c24deff717faf4c094 100755
--- a/source/build-aux/config.sub
+++ b/source/build-aux/config.sub
@@ -1,10 +1,10 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2021 Free Software Foundation, Inc.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2021-12-25'
+timestamp='2022-01-03'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -76,7 +76,7 @@ Report bugs and patches to <config-patches@gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2021 Free Software Foundation, Inc.
+Copyright 1992-2022 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
diff --git a/source/doc/ChangeLog b/source/doc/ChangeLog
index e63d42f6ebf46796c84f9a87c9f821894d53c252..6c8bbc6863ef90c1b740a4158be7fdd83a32155c 100644
--- a/source/doc/ChangeLog
+++ b/source/doc/ChangeLog
@@ -1,3 +1,7 @@
+2022-05-28  Karl Berry  <karl@tug.org>
+
+	* tlbuild.texi (Build one package): typo, buil -> build.
+
 2022-02-19  Karl Berry  <karl@freefriends.org>
 
 	* tlbuild.texi (Prerequisites): C++11 required by HarfBuzz;
diff --git a/source/doc/tlbuild.info b/source/doc/tlbuild.info
index 907ec4bf2469133c384caab7e3647a7081d7d504..c273c1b874d31473b7b712e771446fa69afa3c8e 100644
--- a/source/doc/tlbuild.info
+++ b/source/doc/tlbuild.info
@@ -1,4 +1,4 @@
-This is tlbuild.info, produced by makeinfo version 6.8 from
+This is tlbuild.info, produced by makeinfo version 5.1 from
 tlbuild.texi.
 
 This file documents the TeX Live build system and more.
@@ -59,7 +59,7 @@ File: tlbuild.info,  Node: Introduction,  Next: Overview of build system,  Prev:
 1 Introduction
 **************
 
-This manual (dated March 2022) corresponds to the TeX Live 2022 release.
+This manual (dated May 2022) corresponds to the TeX Live 2022 release.
 
    This manual is aimed at system installers and programmers, and
 focuses on how to configure, build, and develop the TeX Live (TL)
@@ -325,8 +325,8 @@ File: tlbuild.info,  Node: Build one package,  Next: Build one engine,  Prev: Bu
 =====================
 
 To build one package, the basic idea is to use the 'configure' option
-'--disable-all-pkgs' (*note --disable-all-pkgs::).  Then all program and
-library modules are configured but none are made.  However, the
+'--disable-all-pkgs' (*note '--disable-all-pkgs'::).  Then all program
+and library modules are configured but none are made.  However, the
 'Makefile's still contain all build rules and dependencies and can be
 invoked to build an individual program or library, first building any
 required libraries.
@@ -355,7 +355,7 @@ the next section.
      cd texk/dvipdfm-x
      make check
 
-     # Run the new binary in the buil tree, finding support files
+     # Run the new binary in the build tree, finding support files
      # in a separate tree for a TeX Live release YYYY
      # (Bourne shell syntax):
      TEXMFROOT=/usr/local/texlive/YYYY \
@@ -1108,13 +1108,13 @@ structure and variation.
 
 * Menu:
 
-* png library::       'libs/libpng'
-* zlib library::      'libs/zlib'
-* freetype library::  'libs/freetype2'
-* kpathsea library::  'texk/kpathsea'
+* 'png' library::       'libs/libpng'
+* 'zlib' library::      'libs/zlib'
+* 'freetype' library::  'libs/freetype2'
+* 'kpathsea' library::  'texk/kpathsea'
 
 
-File: tlbuild.info,  Node: png library,  Next: zlib library,  Up: Library modules
+File: tlbuild.info,  Node: 'png' library,  Next: 'zlib' library,  Up: Library modules
 
 6.4.1 The 'png' library in 'libs/libpng'
 ----------------------------------------
@@ -1170,7 +1170,7 @@ the 'make' rules to rebuild the library.
 flags required for the system library.
 
 
-File: tlbuild.info,  Node: zlib library,  Next: freetype library,  Prev: png library,  Up: Library modules
+File: tlbuild.info,  Node: 'zlib' library,  Next: 'freetype' library,  Prev: 'png' library,  Up: Library modules
 
 6.4.2 The 'zlib' library in 'libs/zlib'
 ---------------------------------------
@@ -1182,7 +1182,7 @@ supplies the configure option '--with-system-zlib', as well as
 locations of the 'zlib' headers and/or library.
 
 
-File: tlbuild.info,  Node: freetype library,  Next: kpathsea library,  Prev: zlib library,  Up: Library modules
+File: tlbuild.info,  Node: 'freetype' library,  Next: 'kpathsea' library,  Prev: 'zlib' library,  Up: Library modules
 
 6.4.3 The 'freetype' library in 'libs/freetype2'
 ------------------------------------------------
@@ -1198,7 +1198,7 @@ system provided by upstream (possibly patched).
 'freetype-config'.
 
 
-File: tlbuild.info,  Node: kpathsea library,  Prev: freetype library,  Up: Library modules
+File: tlbuild.info,  Node: 'kpathsea' library,  Prev: 'freetype' library,  Up: Library modules
 
 6.4.4 The 'kpathsea' library in 'texk/kpathsea'
 -----------------------------------------------
@@ -1236,13 +1236,13 @@ for a few of the programs in TL.
 
 * Menu:
 
-* t1utils package:: 'utils/t1utils'
-* xindy package::   'utils/xindy'
-* xdvik package::   'texk/xdvik'
-* asymptote::       'utils/asymptote'
+* 't1utils' package:: 'utils/t1utils'
+* 'xindy' package::   'utils/xindy'
+* 'xdvik' package::   'texk/xdvik'
+* 'asymptote'::       'utils/asymptote'
 
 
-File: tlbuild.info,  Node: t1utils package,  Next: xindy package,  Up: Program modules
+File: tlbuild.info,  Node: 't1utils' package,  Next: 'xindy' package,  Up: Program modules
 
 6.5.1 The 't1utils' package in 'utils/t1utils'
 ----------------------------------------------
@@ -1258,7 +1258,7 @@ specifying the module name without any dependencies, and supplies the
 configure option '--disable-t1utils'.
 
 
-File: tlbuild.info,  Node: xindy package,  Next: xdvik package,  Prev: t1utils package,  Up: Program modules
+File: tlbuild.info,  Node: 'xindy' package,  Next: 'xdvik' package,  Prev: 't1utils' package,  Up: Program modules
 
 6.5.2 The 'xindy' package in 'utils/xindy'
 ------------------------------------------
@@ -1284,7 +1284,7 @@ built if explicitly enabled by the user with 'configure --enable-xindy'
 included by 'configure.ac'.
 
 
-File: tlbuild.info,  Node: xdvik package,  Next: asymptote,  Prev: xindy package,  Up: Program modules
+File: tlbuild.info,  Node: 'xdvik' package,  Next: 'asymptote',  Prev: 'xindy' package,  Up: Program modules
 
 6.5.3 The 'xdvik' package in 'texk/xdvik'
 -----------------------------------------
@@ -1309,7 +1309,7 @@ system whereas the auxiliary program 'squeeze/squeeze' has to run on the
 also seen at the top level.
 
 
-File: tlbuild.info,  Node: asymptote,  Prev: xdvik package,  Up: Program modules
+File: tlbuild.info,  Node: 'asymptote',  Prev: 'xdvik' package,  Up: Program modules
 
 6.5.4 The subdirectory 'utils/asymptote'
 ----------------------------------------
@@ -1589,21 +1589,21 @@ Here are the global configure options.
 
 * Menu:
 
-* --disable-native-texlive-build::
-* --prefix --bindir ...::
-* --disable-largefile::
-* --disable-missing::
-* --enable-compiler-warnings=LEVEL::
-* --enable-cxx-runtime-hack::
-* --enable-maintainer-mode::
-* --enable-multiplatform::
-* --enable-shared::
-* --enable-silent-rules::
-* --without-ln-s::
-* --without-x::
+* '--disable-native-texlive-build'::
+* '--prefix' '--bindir' ...::
+* '--disable-largefile'::
+* '--disable-missing'::
+* '--enable-compiler-warnings='LEVEL::
+* '--enable-cxx-runtime-hack'::
+* '--enable-maintainer-mode'::
+* '--enable-multiplatform'::
+* '--enable-shared'::
+* '--enable-silent-rules'::
+* '--without-ln-s'::
+* '--without-x'::
 
 
-File: tlbuild.info,  Node: --disable-native-texlive-build,  Next: --prefix --bindir ...,  Up: Global configure options
+File: tlbuild.info,  Node: '--disable-native-texlive-build',  Next: '--prefix' '--bindir' ...,  Up: Global configure options
 
 7.1.1 '--disable-native-texlive-build'
 --------------------------------------
@@ -1624,7 +1624,7 @@ also be built independently from the TL tree (such as 'utils/xindy' and
 TL-specific adaptations, such as installation paths.
 
 
-File: tlbuild.info,  Node: --prefix --bindir ...,  Next: --disable-largefile,  Prev: --disable-native-texlive-build,  Up: Global configure options
+File: tlbuild.info,  Node: '--prefix' '--bindir' ...,  Next: '--disable-largefile',  Prev: '--disable-native-texlive-build',  Up: Global configure options
 
 7.1.2 '--prefix', '--bindir', ...
 ---------------------------------
@@ -1637,7 +1637,7 @@ set, on the 'make' command line (*note Installation in a temporary
 location: (automake)Staged Installs.).
 
 
-File: tlbuild.info,  Node: --disable-largefile,  Next: --disable-missing,  Prev: --prefix --bindir ...,  Up: Global configure options
+File: tlbuild.info,  Node: '--disable-largefile',  Next: '--disable-missing',  Prev: '--prefix' '--bindir' ...,  Up: Global configure options
 
 7.1.3 '--disable-largefile'
 ---------------------------
@@ -1651,7 +1651,7 @@ specifications.
 'pdftex' or PostScript files created by 'dvips'.
 
 
-File: tlbuild.info,  Node: --disable-missing,  Next: --enable-compiler-warnings=LEVEL,  Prev: --disable-largefile,  Up: Global configure options
+File: tlbuild.info,  Node: '--disable-missing',  Next: '--enable-compiler-warnings='LEVEL,  Prev: '--disable-largefile',  Up: Global configure options
 
 7.1.4 '--disable-missing'
 -------------------------
@@ -1661,7 +1661,7 @@ feature must be disabled, e.g., due to missing libraries.  This can help
 when figuring out a specific (sub)set of modules to enable.
 
 
-File: tlbuild.info,  Node: --enable-compiler-warnings=LEVEL,  Next: --enable-cxx-runtime-hack,  Prev: --disable-missing,  Up: Global configure options
+File: tlbuild.info,  Node: '--enable-compiler-warnings='LEVEL,  Next: '--enable-cxx-runtime-hack',  Prev: '--disable-missing',  Up: Global configure options
 
 7.1.5 '--enable-compiler-warnings='LEVEL
 ----------------------------------------
@@ -1674,7 +1674,7 @@ variables are not consistently used in all library and program modules.
 At present, these warning flags assume options from the GNU compilers.
 
 
-File: tlbuild.info,  Node: --enable-cxx-runtime-hack,  Next: --enable-maintainer-mode,  Prev: --enable-compiler-warnings=LEVEL,  Up: Global configure options
+File: tlbuild.info,  Node: '--enable-cxx-runtime-hack',  Next: '--enable-maintainer-mode',  Prev: '--enable-compiler-warnings='LEVEL,  Up: Global configure options
 
 7.1.6 '--enable-cxx-runtime-hack'
 ---------------------------------
@@ -1684,7 +1684,7 @@ statically link with 'libstdc++', thus improving portability of the
 resulting binary.  *Note Macros for compilers::.
 
 
-File: tlbuild.info,  Node: --enable-maintainer-mode,  Next: --enable-multiplatform,  Prev: --enable-cxx-runtime-hack,  Up: Global configure options
+File: tlbuild.info,  Node: '--enable-maintainer-mode',  Next: '--enable-multiplatform',  Prev: '--enable-cxx-runtime-hack',  Up: Global configure options
 
 7.1.7 '--enable-maintainer-mode'
 --------------------------------
@@ -1696,7 +1696,7 @@ rebuilds infrastructure files as needed.  *Note 'missing' and
 'AM_MAINTAINER_MODE': (automake)maintainer-mode.
 
 
-File: tlbuild.info,  Node: --enable-multiplatform,  Next: --enable-shared,  Prev: --enable-maintainer-mode,  Up: Global configure options
+File: tlbuild.info,  Node: '--enable-multiplatform',  Next: '--enable-shared',  Prev: '--enable-maintainer-mode',  Up: Global configure options
 
 7.1.8 '--enable-multiplatform'
 ------------------------------
@@ -1709,7 +1709,7 @@ the values for 'bindir' and 'libdir' are automatically propagated to all
 subdirectories.
 
 
-File: tlbuild.info,  Node: --enable-shared,  Next: --enable-silent-rules,  Prev: --enable-multiplatform,  Up: Global configure options
+File: tlbuild.info,  Node: '--enable-shared',  Next: '--enable-silent-rules',  Prev: '--enable-multiplatform',  Up: Global configure options
 
 7.1.9 '--enable-shared'
 -----------------------
@@ -1719,7 +1719,7 @@ Build shared versions of the TeX-specific libraries such as
 '--disable-native-texlive-build' must also be specified).
 
 
-File: tlbuild.info,  Node: --enable-silent-rules,  Next: --without-ln-s,  Prev: --enable-shared,  Up: Global configure options
+File: tlbuild.info,  Node: '--enable-silent-rules',  Next: '--without-ln-s',  Prev: '--enable-shared',  Up: Global configure options
 
 7.1.10 '--enable-silent-rules'
 ------------------------------
@@ -1730,7 +1730,7 @@ can specify 'V=1' on the 'make' command line to get more verbosity, or
 'V=0' to get less, regardless of this option.
 
 
-File: tlbuild.info,  Node: --without-ln-s,  Next: --without-x,  Prev: --enable-silent-rules,  Up: Global configure options
+File: tlbuild.info,  Node: '--without-ln-s',  Next: '--without-x',  Prev: '--enable-silent-rules',  Up: Global configure options
 
 7.1.11 '--without-ln-s'
 -----------------------
@@ -1740,7 +1740,7 @@ for a Unix-like system.  However, 'make install' will not create
 anything useful, and might fail.
 
 
-File: tlbuild.info,  Node: --without-x,  Prev: --without-ln-s,  Up: Global configure options
+File: tlbuild.info,  Node: '--without-x',  Prev: '--without-ln-s',  Up: Global configure options
 
 7.1.12 '--without-x'
 --------------------
@@ -1757,18 +1757,18 @@ Here are (some of) the program-specific 'configure' options.
 
 * Menu:
 
-* --enable-PROG --disable-PROG::
-* --disable-all-pkgs::
-* Configure options for texk/web2c::
-* Configure options for texk/bibtex-x::
-* Configure options for texk/dvipdfm-x::
-* Configure options for texk/dvisvgm::
-* Configure options for texk/texlive::
-* Configure options for texk/xdvik::
-* Configure options for utils/xindy::
+* '--enable-PROG' '--disable-PROG'::
+* '--disable-all-pkgs'::
+* Configure options for 'texk/web2c'::
+* Configure options for 'texk/bibtex-x'::
+* Configure options for 'texk/dvipdfm-x'::
+* Configure options for 'texk/dvisvgm'::
+* Configure options for 'texk/texlive'::
+* Configure options for 'texk/xdvik'::
+* Configure options for 'utils/xindy'::
 
 
-File: tlbuild.info,  Node: --enable-PROG --disable-PROG,  Next: --disable-all-pkgs,  Up: Program-specific configure options
+File: tlbuild.info,  Node: '--enable-PROG' '--disable-PROG',  Next: '--disable-all-pkgs',  Up: Program-specific configure options
 
 7.2.1 '--enable-PROG', '--disable-PROG'
 ---------------------------------------
@@ -1776,7 +1776,7 @@ File: tlbuild.info,  Node: --enable-PROG --disable-PROG,  Next: --disable-all-pk
 Do or do not build and install the program(s) of module 'PROG'.
 
 
-File: tlbuild.info,  Node: --disable-all-pkgs,  Next: Configure options for texk/web2c,  Prev: --enable-PROG --disable-PROG,  Up: Program-specific configure options
+File: tlbuild.info,  Node: '--disable-all-pkgs',  Next: Configure options for 'texk/web2c',  Prev: '--enable-PROG' '--disable-PROG',  Up: Program-specific configure options
 
 7.2.2 '--disable-all-pkgs'
 --------------------------
@@ -1793,7 +1793,7 @@ explicitly disabled or specify 'disable' in their 'ac/withenable.ac'
 fragment.
 
 
-File: tlbuild.info,  Node: Configure options for texk/web2c,  Next: Configure options for texk/bibtex-x,  Prev: --disable-all-pkgs,  Up: Program-specific configure options
+File: tlbuild.info,  Node: Configure options for 'texk/web2c',  Next: Configure options for 'texk/bibtex-x',  Prev: '--disable-all-pkgs',  Up: Program-specific configure options
 
 7.2.3 Configure options for 'texk/web2c'
 ----------------------------------------
@@ -1860,7 +1860,7 @@ native TeX Live build.  Defaults are defined in
 Do not build the 'SyncTeX' library and tool.
 
 
-File: tlbuild.info,  Node: Configure options for texk/bibtex-x,  Next: Configure options for texk/dvipdfm-x,  Prev: Configure options for texk/web2c,  Up: Program-specific configure options
+File: tlbuild.info,  Node: Configure options for 'texk/bibtex-x',  Next: Configure options for 'texk/dvipdfm-x',  Prev: Configure options for 'texk/web2c',  Up: Program-specific configure options
 
 7.2.4 Configure options for 'texk/bibtex-x'
 -------------------------------------------
@@ -1876,7 +1876,7 @@ Do not build the 'bibtexu' program (building 'bibtexu' requires 'ICU'
 libraries).
 
 
-File: tlbuild.info,  Node: Configure options for texk/dvipdfm-x,  Next: Configure options for texk/dvisvgm,  Prev: Configure options for texk/bibtex-x,  Up: Program-specific configure options
+File: tlbuild.info,  Node: Configure options for 'texk/dvipdfm-x',  Next: Configure options for 'texk/dvisvgm',  Prev: Configure options for 'texk/bibtex-x',  Up: Program-specific configure options
 
 7.2.5 Configure options for 'texk/dvipdfm-x'
 --------------------------------------------
@@ -1894,7 +1894,7 @@ Do not build the 'dvipdfmx' program or make the 'dvipdfm' symlink.
 Do not build the 'xdvipdfmx' program.
 
 
-File: tlbuild.info,  Node: Configure options for texk/dvisvgm,  Next: Configure options for texk/texlive,  Prev: Configure options for texk/dvipdfm-x,  Up: Program-specific configure options
+File: tlbuild.info,  Node: Configure options for 'texk/dvisvgm',  Next: Configure options for 'texk/texlive',  Prev: Configure options for 'texk/dvipdfm-x',  Up: Program-specific configure options
 
 7.2.6 Configure options for 'texk/dvisvgm'
 ------------------------------------------
@@ -1914,7 +1914,7 @@ result can crash due to library incompatibilities, e.g., on CentOS 5.
 Specify non-standard locations of the Ghostscript headers and library.
 
 
-File: tlbuild.info,  Node: Configure options for texk/texlive,  Next: Configure options for texk/xdvik,  Prev: Configure options for texk/dvisvgm,  Up: Program-specific configure options
+File: tlbuild.info,  Node: Configure options for 'texk/texlive',  Next: Configure options for 'texk/xdvik',  Prev: Configure options for 'texk/dvisvgm',  Up: Program-specific configure options
 
 7.2.7 Configure options for 'texk/texlive'
 ------------------------------------------
@@ -1924,7 +1924,7 @@ Do not install the "linked scripts" (*note Linked scripts::), except for
 the TL scripts required to run 'texlinks'.
 
 
-File: tlbuild.info,  Node: Configure options for texk/xdvik,  Next: Configure options for utils/xindy,  Prev: Configure options for texk/texlive,  Up: Program-specific configure options
+File: tlbuild.info,  Node: Configure options for 'texk/xdvik',  Next: Configure options for 'utils/xindy',  Prev: Configure options for 'texk/texlive',  Up: Program-specific configure options
 
 7.2.8 Configure options for 'texk/xdvik'
 ----------------------------------------
@@ -1941,7 +1941,7 @@ Use XInput 2.1 "smooth scrolling" if available (default: yes, except for
 a native TL build).
 
 
-File: tlbuild.info,  Node: Configure options for utils/xindy,  Prev: Configure options for texk/xdvik,  Up: Program-specific configure options
+File: tlbuild.info,  Node: Configure options for 'utils/xindy',  Prev: Configure options for 'texk/xdvik',  Up: Program-specific configure options
 
 7.2.9 Configure options for 'utils/xindy'
 -----------------------------------------
@@ -1987,10 +1987,10 @@ required system libraries and bails out if tests fail.
 
 * Menu:
 
-* Configure options for kpathsea::
+* Configure options for 'kpathsea'::
 
 
-File: tlbuild.info,  Node: Configure options for kpathsea,  Up: Library-specific configure options
+File: tlbuild.info,  Node: Configure options for 'kpathsea',  Up: Library-specific configure options
 
 7.3.1 Configure options for 'kpathsea'
 --------------------------------------
@@ -2014,7 +2014,7 @@ is one of:
      (TFM file)
 
 to generate the specified type of file dynamically.  The default can be
-overridden by the user in any case (*note kpathsea library::).
+overridden by the user in any case (*note 'kpathsea' library::).
 
 
 File: tlbuild.info,  Node: Variables for configure,  Prev: Library-specific configure options,  Up: Configure options
@@ -5194,11 +5194,11 @@ _pkg_..., and *note option: tlmgr option. actions.
 
 * Menu:
 
-* tlmgr Machine-readable update and install output::
-* tlmgr Machine-readable option output::
+* tlmgr Machine-readable 'update' and 'install' output::
+* tlmgr Machine-readable 'option' output::
 
 
-File: tlbuild.info,  Node: tlmgr Machine-readable update and install output,  Next: tlmgr Machine-readable option output,  Up: tlmgr MACHINE-READABLE OUTPUT
+File: tlbuild.info,  Node: tlmgr Machine-readable 'update' and 'install' output,  Next: tlmgr Machine-readable 'option' output,  Up: tlmgr MACHINE-READABLE OUTPUT
 
 B.12.1 Machine-readable 'update' and 'install' output
 -----------------------------------------------------
@@ -5316,7 +5316,7 @@ _esttot_
      The estimated total time.
 
 
-File: tlbuild.info,  Node: tlmgr Machine-readable option output,  Prev: tlmgr Machine-readable update and install output,  Up: tlmgr MACHINE-READABLE OUTPUT
+File: tlbuild.info,  Node: tlmgr Machine-readable 'option' output,  Prev: tlmgr Machine-readable 'update' and 'install' output,  Up: tlmgr MACHINE-READABLE OUTPUT
 
 B.12.2 Machine-readable 'option' output
 ---------------------------------------
@@ -5437,114 +5437,116 @@ Index
 �[index�]
 * Menu:
 
-* $@ target in normal make rules:        Prerequisites.       (line  13)
-* --bindir configure option:             --prefix --bindir ....
+* '$@' target in normal 'make' rules:    Prerequisites.       (line  13)
+* --bindir configure option:             '--prefix' '--bindir' ....
                                                               (line   6)
-* --bindir configure option <1>:         --enable-multiplatform.
+* --bindir configure option <1>:         '--enable-multiplatform'.
                                                               (line   6)
 * --build=HOST:                          Cross configuring.   (line   6)
 * --disable-all-packages:                Build one package.   (line   6)
-* --disable-all-pkgs:                    --disable-all-pkgs.  (line   6)
-* --disable-bibtex8:                     Configure options for texk/bibtex-x.
+* --disable-all-pkgs:                    '--disable-all-pkgs'.
+                                                              (line   6)
+* --disable-bibtex8:                     Configure options for 'texk/bibtex-x'.
                                                               (line   9)
-* --disable-bibtexu:                     Configure options for texk/bibtex-x.
+* --disable-bibtexu:                     Configure options for 'texk/bibtex-x'.
                                                               (line  12)
-* --disable-dump-share:                  Configure options for texk/web2c.
+* --disable-dump-share:                  Configure options for 'texk/web2c'.
                                                               (line  27)
-* --disable-dvipdfmx:                    Configure options for texk/dvipdfm-x.
+* --disable-dvipdfmx:                    Configure options for 'texk/dvipdfm-x'.
                                                               (line  12)
-* --disable-etex-synctex:                Configure options for texk/web2c.
+* --disable-etex-synctex:                Configure options for 'texk/web2c'.
                                                               (line  59)
-* --disable-ipc:                         Configure options for texk/web2c.
+* --disable-ipc:                         Configure options for 'texk/web2c'.
                                                               (line  31)
-* --disable-largefile:                   --disable-largefile. (line   6)
-* --disable-linked-scripts:              Configure options for texk/texlive.
+* --disable-largefile:                   '--disable-largefile'.
+                                                              (line   6)
+* --disable-linked-scripts:              Configure options for 'texk/texlive'.
                                                               (line   6)
-* --disable-mf-nowin:                    Configure options for texk/web2c.
+* --disable-mf-nowin:                    Configure options for 'texk/web2c'.
                                                               (line  34)
-* --disable-missing:                     --disable-missing.   (line   6)
-* --disable-native-texlive-build:        --disable-native-texlive-build.
+* --disable-missing:                     '--disable-missing'. (line   6)
+* --disable-native-texlive-build:        '--disable-native-texlive-build'.
                                                               (line   6)
-* --disable-PROG:                        --enable-PROG --disable-PROG.
+* --disable-PROG:                        '--enable-PROG' '--disable-PROG'.
                                                               (line   6)
-* --disable-synctex:                     Configure options for texk/web2c.
+* --disable-synctex:                     Configure options for 'texk/web2c'.
                                                               (line  64)
-* --disable-tex:                         Configure options for texk/web2c.
+* --disable-tex:                         Configure options for 'texk/web2c'.
                                                               (line  37)
-* --disable-web-progs:                   Configure options for texk/web2c.
+* --disable-web-progs:                   Configure options for 'texk/web2c'.
                                                               (line  41)
-* --disable-xdvipdfmx:                   Configure options for texk/dvipdfm-x.
+* --disable-xdvipdfmx:                   Configure options for 'texk/dvipdfm-x'.
                                                               (line  15)
-* --enable-*win for Metafont window support: Configure options for texk/web2c.
+* --enable-*win for Metafont window support: Configure options for 'texk/web2c'.
                                                               (line  55)
-* --enable-auto-core:                    Configure options for texk/web2c.
+* --enable-auto-core:                    Configure options for 'texk/web2c'.
                                                               (line  45)
-* --enable-compiler-warnings=LEVEL:      --enable-compiler-warnings=LEVEL.
+* --enable-compiler-warnings=LEVEL:      '--enable-compiler-warnings='LEVEL.
                                                               (line   6)
 * --enable-cxx-runtime-hack:             Macros for compilers.
                                                               (line  29)
-* --enable-etex:                         Configure options for texk/web2c.
+* --enable-etex:                         Configure options for 'texk/web2c'.
                                                               (line  37)
-* --enable-libtool-hack:                 Configure options for texk/web2c.
+* --enable-libtool-hack:                 Configure options for 'texk/web2c'.
                                                               (line  50)
 * --enable-maintainer-mode:              Build system tools.  (line  28)
-* --enable-maintainer-mode <1>:          --enable-maintainer-mode.
+* --enable-maintainer-mode <1>:          '--enable-maintainer-mode'.
                                                               (line   6)
 * --enable-missing to ignore dependencies: Build one package. (line  94)
-* --enable-mktextfm-default:             kpathsea library.    (line  18)
-* --enable-multiplatform:                --enable-multiplatform.
+* --enable-mktextfm-default:             'kpathsea' library.  (line  18)
+* --enable-multiplatform:                '--enable-multiplatform'.
                                                               (line   6)
-* --enable-PROG:                         --enable-PROG --disable-PROG.
+* --enable-PROG:                         '--enable-PROG' '--disable-PROG'.
                                                               (line   6)
-* --enable-shared:                       --enable-shared.     (line   6)
-* --enable-silent-rules:                 --enable-silent-rules.
+* --enable-shared:                       '--enable-shared'.   (line   6)
+* --enable-silent-rules:                 '--enable-silent-rules'.
                                                               (line   6)
-* --enable-tex-synctex:                  Configure options for texk/web2c.
+* --enable-tex-synctex:                  Configure options for 'texk/web2c'.
                                                               (line  59)
-* --enable-texlive-build:                --disable-native-texlive-build.
+* --enable-texlive-build:                '--disable-native-texlive-build'.
                                                               (line  15)
-* --enable-xi2-scrolling:                Configure options for texk/xdvik.
+* --enable-xi2-scrolling:                Configure options for 'texk/xdvik'.
                                                               (line  13)
-* --enable-xindy-docs:                   Configure options for utils/xindy.
+* --enable-xindy-docs:                   Configure options for 'utils/xindy'.
                                                               (line  10)
-* --enable-xindy-rules:                  Configure options for utils/xindy.
+* --enable-xindy-rules:                  Configure options for 'utils/xindy'.
                                                               (line   6)
 * --host=HOST:                           Cross configuring.   (line   6)
-* --libdir configure option:             --enable-multiplatform.
+* --libdir configure option:             '--enable-multiplatform'.
                                                               (line   6)
 * --no-print-directory GNU make option:  Build one engine.    (line  56)
-* --prefix configure option:             --prefix --bindir ....
+* --prefix configure option:             '--prefix' '--bindir' ....
                                                               (line   6)
-* --with-banner-add=STR:                 Configure options for texk/web2c.
+* --with-banner-add=STR:                 Configure options for 'texk/web2c'.
                                                               (line   6)
-* --with-clisp-runtime=FILENAME:         Configure options for utils/xindy.
+* --with-clisp-runtime=FILENAME:         Configure options for 'utils/xindy'.
                                                               (line  14)
-* --with-editor=CMD:                     Configure options for texk/web2c.
+* --with-editor=CMD:                     Configure options for 'texk/web2c'.
                                                               (line  11)
-* --with-fontconfig-includes=DIR:        Configure options for texk/web2c.
+* --with-fontconfig-includes=DIR:        Configure options for 'texk/web2c'.
                                                               (line  16)
-* --with-fontconfig-libdir=DIR:          Configure options for texk/web2c.
+* --with-fontconfig-libdir=DIR:          Configure options for 'texk/web2c'.
                                                               (line  16)
-* --with-gs=FILENAME:                    Configure options for texk/xdvik.
+* --with-gs=FILENAME:                    Configure options for 'texk/xdvik'.
                                                               (line   6)
 * --with-LIB-includes=DIR, -libdir:      Library-specific configure options.
                                                               (line  16)
-* --with-libgs-includes, -libdir:        Configure options for texk/dvisvgm.
+* --with-libgs-includes, -libdir:        Configure options for 'texk/dvisvgm'.
                                                               (line  17)
-* --with-system-kpathsea:                kpathsea library.    (line  13)
+* --with-system-kpathsea:                'kpathsea' library.  (line  13)
 * --with-system-LIB:                     Adding a new generic library module.
                                                               (line  34)
 * --with-system-LIB <1>:                 Library-specific configure options.
                                                               (line   9)
-* --with-system-libgs:                   Configure options for texk/dvisvgm.
+* --with-system-libgs:                   Configure options for 'texk/dvisvgm'.
                                                               (line   6)
-* --with-xdvi-x-toolkit:                 xdvik package.       (line  21)
-* --with-xdvi-x-toolkit=KIT:             Configure options for texk/xdvik.
+* --with-xdvi-x-toolkit:                 'xdvik' package.     (line  21)
+* --with-xdvi-x-toolkit=KIT:             Configure options for 'texk/xdvik'.
                                                               (line   9)
-* --without-libgs:                       Configure options for texk/dvisvgm.
+* --without-libgs:                       Configure options for 'texk/dvisvgm'.
                                                               (line  12)
-* --without-ln-s:                        --without-ln-s.      (line   6)
-* --without-x:                           --without-x.         (line   6)
+* --without-ln-s:                        '--without-ln-s'.    (line   6)
+* --without-x:                           '--without-x'.       (line   6)
 * -C configure option:                   Build in parallel.   (line  11)
 * -j make option:                        Build in parallel.   (line   6)
 * ac/withenable.ac:                      Adding a new program module.
@@ -5558,14 +5560,14 @@ Index
 * adding a new TeX-specific library:     Adding a new TeX-specific library module.
                                                               (line   6)
 * adding to TeX Live:                    Extending TeX Live.  (line   6)
-* am/ top-level directory:               Top-level directories.
+* 'am/' top-level directory:             Top-level directories.
                                                               (line  14)
 * ANSI C:                                Declarations and definitions.
                                                               (line   6)
-* ApplicationServices Mac framework, required by xetex: Prerequisites.
+* 'ApplicationServices' Mac framework, required by 'xetex': Prerequisites.
                                                               (line  40)
 * asymptote:                             Linked scripts.      (line  23)
-* asymptote <1>:                         asymptote.           (line   6)
+* asymptote <1>:                         'asymptote'.         (line   6)
 * Autoconf:                              Overview of build system.
                                                               (line   6)
 * autoconf macros:                       Autoconf macros.     (line   6)
@@ -5574,11 +5576,11 @@ Index
 * autoreconf, for new program:           Adding a new program module.
                                                               (line  76)
 * biber:                                 Linked scripts.      (line  23)
-* bibtex-x:                              Configure options for texk/bibtex-x.
+* bibtex-x:                              Configure options for 'texk/bibtex-x'.
                                                               (line   6)
-* bibtex8:                               Configure options for texk/bibtex-x.
+* bibtex8:                               Configure options for 'texk/bibtex-x'.
                                                               (line   6)
-* bibtexu:                               Configure options for texk/bibtex-x.
+* bibtexu:                               Configure options for 'texk/bibtex-x'.
                                                               (line   6)
 * BSD distro:                            Distro builds.       (line   6)
 * build directory, required:             Building.            (line  17)
@@ -5589,7 +5591,7 @@ Index
 * Build script:                          Building.            (line   6)
 * build system, design of:               Overview of build system.
                                                               (line   6)
-* build-aux/ top-level directory:        Top-level directories.
+* 'build-aux/' top-level directory:      Top-level directories.
                                                               (line  30)
 * BUILDCC, BUILDCFLAGS, ...:             Cross configuring.   (line  42)
 * building:                              Building.            (line   6)
@@ -5601,8 +5603,8 @@ Index
                                                               (line   6)
 * C99, avoided:                          Declarations and definitions.
                                                               (line   6)
-* cache file, for configure:             Build in parallel.   (line  11)
-* cache for configure:                   Build in parallel.   (line   6)
+* cache file, for 'configure':           Build in parallel.   (line  11)
+* cache for 'configure':                 Build in parallel.   (line   6)
 * callexe.c:                             Macros for Windows.  (line  32)
 * CC:                                    Variables for configure.
                                                               (line  10)
@@ -5616,45 +5618,45 @@ Index
                                                               (line  18)
 * CLISP:                                 Variables for configure.
                                                               (line  17)
-* CLISP <1>:                             Configure options for utils/xindy.
+* CLISP <1>:                             Configure options for 'utils/xindy'.
                                                               (line  14)
-* clisp, required by xindy:              Prerequisites.       (line  44)
-* Cocoa Mac framework, required by xetex: Prerequisites.      (line  40)
+* 'clisp', required by 'xindy':          Prerequisites.       (line  44)
+* 'Cocoa' Mac framework, required by 'xetex': Prerequisites.  (line  40)
 * coding conventions:                    Coding conventions.  (line   6)
 * compilers, C and C++11:                Prerequisites.       (line   6)
 * config.guess, config.sub, ...:         Top-level directories.
                                                               (line  30)
-* configure options:                     Configure options.   (line   6)
-* configure options, for bibtex-x:       Configure options for texk/bibtex-x.
+* 'configure' options:                   Configure options.   (line   6)
+* 'configure' options, for 'bibtex-x':   Configure options for 'texk/bibtex-x'.
                                                               (line   6)
-* configure options, for dvipdfm-x:      Configure options for texk/dvipdfm-x.
+* 'configure' options, for 'dvipdfm-x':  Configure options for 'texk/dvipdfm-x'.
                                                               (line   6)
-* configure options, for dvisvgm:        Configure options for texk/dvisvgm.
+* 'configure' options, for 'dvisvgm':    Configure options for 'texk/dvisvgm'.
                                                               (line   6)
-* configure options, for kpathsea:       Configure options for kpathsea.
+* 'configure' options, for 'kpathsea':   Configure options for 'kpathsea'.
                                                               (line   6)
-* configure options, for texk/texlive:   Configure options for texk/texlive.
+* 'configure' options, for 'texk/texlive': Configure options for 'texk/texlive'.
                                                               (line   6)
-* configure options, for web2c:          Configure options for texk/web2c.
+* 'configure' options, for 'web2c':      Configure options for 'texk/web2c'.
                                                               (line   6)
-* configure options, for xdvik:          Configure options for texk/xdvik.
+* 'configure' options, for 'xdvik':      Configure options for 'texk/xdvik'.
                                                               (line   6)
-* configure options, for xindy:          Configure options for utils/xindy.
+* 'configure' options, for 'xindy':      Configure options for 'utils/xindy'.
                                                               (line   6)
-* configure options, global:             Global configure options.
+* 'configure' options, global:           Global configure options.
                                                               (line   6)
-* configure options, library-specific:   Library-specific configure options.
+* 'configure' options, library-specific: Library-specific configure options.
                                                               (line   6)
-* configure options, program-specific:   Program-specific configure options.
+* 'configure' options, program-specific: Program-specific configure options.
                                                               (line   6)
-* configure problems, work around by removing: Build one package.
+* 'configure' problems, work around by removing: Build one package.
                                                               (line 106)
-* configure variables:                   Variables for configure.
+* 'configure' variables:                 Variables for configure.
                                                               (line   6)
 * configure.ac:                          Adding a new program module.
                                                               (line  45)
 * configuring, for cross compilation:    Cross configuring.   (line   6)
-* const:                                 Const.               (line   6)
+* 'const':                               Const.               (line   6)
 * continuous integration:                Continuous integration.
                                                               (line   6)
 * conventions, coding:                   Coding conventions.  (line   6)
@@ -5663,7 +5665,7 @@ Index
 * cross compilation:                     Cross compilation.   (line   6)
 * cross compilation configuring:         Cross configuring.   (line   6)
 * cross compilation problems:            Cross problems.      (line   6)
-* cross compilation, with host binary:   xdvik package.       (line  14)
+* cross compilation, with host binary:   'xdvik' package.     (line  14)
 * ctangle:                               Cross problems.      (line  26)
 * CXX:                                   Variables for configure.
                                                               (line  11)
@@ -5673,38 +5675,39 @@ Index
 * declarations before statements, avoiding: Declarations and definitions.
                                                               (line   6)
 * dependencies, with several output files: Build in parallel. (line   6)
-* DESTDIR:                               --prefix --bindir ....
+* DESTDIR:                               '--prefix' '--bindir' ....
                                                               (line   9)
 * directories, for installation:         Installation directories.
                                                               (line   6)
 * directories, top-level:                Top-level directories.
                                                               (line   6)
 * discards qualifiers warning:           Const.               (line  30)
-* dist and distcheck targets for make:   Build distribution.  (line   6)
+* 'dist' and 'distcheck' targets for 'make': Build distribution.
+                                                              (line   6)
 * distribution tarball, making:          Build distribution.  (line   6)
 * distro, building for:                  Distro builds.       (line   6)
-* dvipdfm-x:                             Configure options for texk/dvipdfm-x.
+* dvipdfm-x:                             Configure options for 'texk/dvipdfm-x'.
                                                               (line   6)
-* dvipdfmx:                              Configure options for texk/dvipdfm-x.
+* dvipdfmx:                              Configure options for 'texk/dvipdfm-x'.
                                                               (line   6)
-* dvisvgm:                               Configure options for texk/dvisvgm.
+* dvisvgm:                               Configure options for 'texk/dvisvgm'.
                                                               (line   6)
-* dvisvgm, requires C++11:               Prerequisites.       (line  17)
+* 'dvisvgm', requires C++11:             Prerequisites.       (line  17)
 * engine, adding new:                    Adding a new engine. (line   6)
 * engine, building one:                  Build one engine.    (line   6)
-* environment variables, for configure:  Configure options.   (line  16)
-* exec_prefix:                           --enable-multiplatform.
+* environment variables, for 'configure': Configure options.  (line  16)
+* exec_prefix:                           '--enable-multiplatform'.
                                                               (line   6)
 * extending TeX Live:                    Extending TeX Live.  (line   6)
-* extern functions:                      Declarations and definitions.
+* 'extern' functions:                    Declarations and definitions.
                                                               (line  41)
 * flags, macros for library and header:  Macros for library and header flags.
                                                               (line   6)
-* fontconfig library, required by xetex: Prerequisites.       (line  40)
-* freetype cross compiling:              Cross problems.      (line  13)
-* freetype library:                      freetype library.    (line   6)
-* FreeType, requires gmake:              Prerequisites.       (line  13)
-* freetype-config:                       freetype library.    (line  13)
+* 'fontconfig' library, required by 'xetex': Prerequisites.   (line  40)
+* 'freetype' cross compiling:            Cross problems.      (line  13)
+* freetype library:                      'freetype' library.  (line   6)
+* FreeType, requires 'gmake':            Prerequisites.       (line  13)
+* freetype-config:                       'freetype' library.  (line  13)
 * freetype-config <1>:                   Variables for configure.
                                                               (line  24)
 * FT2_CONFIG:                            Variables for configure.
@@ -5714,14 +5717,14 @@ Index
                                                               (line   6)
 * generic library module, adding:        Adding a new generic library module.
                                                               (line   6)
-* Ghostscript location for Xdvik:        Configure options for texk/xdvik.
+* Ghostscript location for Xdvik:        Configure options for 'texk/xdvik'.
                                                               (line   6)
 * git-svn:                               Transfer from Subversion to Github.
                                                               (line   6)
-* global configure options:              Global configure options.
+* global 'configure' options:            Global configure options.
                                                               (line   6)
-* gmake, required:                       Prerequisites.       (line  13)
-* GNU make, required:                    Prerequisites.       (line  13)
+* 'gmake', required:                     Prerequisites.       (line  13)
+* GNU 'make', required:                  Prerequisites.       (line  13)
 * GNU tools, needed for building:        Build system tools.  (line   6)
 * GNU/Linux distro:                      Distro builds.       (line   6)
 * Gnulib, used for common files:         Top-level directories.
@@ -5736,23 +5739,23 @@ Index
 * ICU_CONFIG:                            Variables for configure.
                                                               (line  22)
 * infrastructure, tools needed for:      Build system tools.  (line   6)
-* inst/ top-level directory:             Top-level directories.
+* 'inst/' top-level directory:           Top-level directories.
                                                               (line  39)
 * install-tl, TeX Live installer:        Installing.          (line   8)
 * installation directories:              Installation directories.
                                                               (line   6)
 * installing:                            Installing.          (line   6)
-* interprocess communication:            Configure options for texk/web2c.
+* interprocess communication:            Configure options for 'texk/web2c'.
                                                               (line  31)
 * introduction:                          Introduction.        (line   6)
-* iteration through sources, by configure and make: Build iteration.
+* iteration through sources, by 'configure' and 'make': Build iteration.
                                                               (line   6)
-* kpathsea library:                      kpathsea library.    (line   6)
-* kpathsea.ac:                           kpathsea library.    (line  18)
-* kpse-libpng-flags.m4:                  png library.         (line  45)
+* kpathsea library:                      'kpathsea' library.  (line   6)
+* kpathsea.ac:                           'kpathsea' library.  (line  18)
+* kpse-libpng-flags.m4:                  'png' library.       (line  46)
 * kpse-pkgs.m4:                          Overview of build system.
                                                               (line  30)
-* kpse-zlib-flags.m4:                    zlib library.        (line   6)
+* kpse-zlib-flags.m4:                    'zlib' library.      (line   6)
 * kpsewhich:                             Variables for configure.
                                                               (line  30)
 * KPSEWHICH:                             Variables for configure.
@@ -5791,7 +5794,7 @@ Index
                                                               (line   8)
 * KPSE_LIBPNG_FLAGS:                     Macros for library and header flags.
                                                               (line  10)
-* KPSE_LIBPNG_FLAGS <1>:                 png library.         (line  45)
+* KPSE_LIBPNG_FLAGS <1>:                 'png' library.       (line  46)
 * kpse_libs_pkgs:                        Adding a new generic library module.
                                                               (line   6)
 * KPSE_LIB_FLAGS:                        Macros for library and header flags.
@@ -5807,10 +5810,10 @@ Index
                                                               (line   6)
 * kpse_texlibs_pkgs:                     Adding a new TeX-specific library module.
                                                               (line  11)
-* KPSE_TRY_LIB:                          png library.         (line  17)
+* KPSE_TRY_LIB:                          'png' library.       (line  18)
 * KPSE_TRY_LIB <1>:                      Adding a new generic library module.
                                                               (line  20)
-* KPSE_TRY_LIBXX:                        png library.         (line  30)
+* KPSE_TRY_LIBXX:                        'png' library.       (line  31)
 * KPSE_TRY_LIBXX <1>:                    Adding a new generic library module.
                                                               (line  20)
 * kpse_utils_pkgs:                       Adding a new program module.
@@ -5820,44 +5823,46 @@ Index
                                                               (line  11)
 * KPSE_WITH_TEXLIB:                      Adding a new TeX-specific library module.
                                                               (line  14)
-* large file support:                    --disable-largefile. (line   6)
+* large file support:                    '--disable-largefile'.
+                                                              (line   6)
 * LATEX:                                 Variables for configure.
                                                               (line  40)
 * layout of sources:                     Layout and infrastructure.
                                                               (line   6)
-* LFS (large file support):              --disable-largefile. (line   6)
-* libexpat, dependency of libfontconfig: Configure options for texk/web2c.
+* LFS (large file support):              '--disable-largefile'.
+                                                              (line   6)
+* libexpat, dependency of 'libfontconfig': Configure options for 'texk/web2c'.
                                                               (line  50)
-* libfontconfig, hack for avoiding linking dependencies: Configure options for texk/web2c.
+* libfontconfig, hack for avoiding linking dependencies: Configure options for 'texk/web2c'.
                                                               (line  50)
 * libfreetype:                           Variables for configure.
                                                               (line  24)
-* libfreetype, and const:                Const.               (line  21)
-* libpng library:                        png library.         (line   6)
+* 'libfreetype', and 'const':            Const.               (line  21)
+* libpng library:                        'png' library.       (line   6)
 * library module, generic, adding:       Adding a new generic library module.
                                                               (line   6)
 * library module, TeX-specific, adding:  Adding a new TeX-specific library module.
                                                               (line   6)
 * library modules:                       Library modules.     (line   6)
-* library-specific configure options:    Library-specific configure options.
+* library-specific 'configure' options:  Library-specific configure options.
                                                               (line   6)
-* libsigsegv, required by xindy:         Prerequisites.       (line  44)
+* 'libsigsegv', required by 'xindy':     Prerequisites.       (line  44)
 * libstc++, statically linking:          Macros for compilers.
                                                               (line  29)
 * Libtool:                               Overview of build system.
                                                               (line   6)
-* libtool, hack for avoiding excessive linking: Configure options for texk/web2c.
+* libtool, hack for avoiding excessive linking: Configure options for 'texk/web2c'.
                                                               (line  50)
-* libXt:                                 Configure options for texk/web2c.
+* libXt:                                 Configure options for 'texk/web2c'.
                                                               (line  22)
 * linked scripts:                        Linked scripts.      (line   6)
 * linking C++ libraries statically:      Macros for compilers.
                                                               (line  29)
-* lisp.run, lisp.exe:                    Configure options for utils/xindy.
+* lisp.run, lisp.exe:                    Configure options for 'utils/xindy'.
                                                               (line  14)
-* LittleEndian architectures:            Configure options for texk/web2c.
+* LittleEndian architectures:            Configure options for 'texk/web2c'.
                                                               (line  27)
-* m4/ top-level directory:               Top-level directories.
+* 'm4/' top-level directory:             Top-level directories.
                                                               (line  14)
 * macros, for compilers:                 Macros for compilers.
                                                               (line   6)
@@ -5872,19 +5877,19 @@ Index
 * MAKE:                                  Variables for configure.
                                                               (line  33)
 * make -t:                               Build system tools.  (line  43)
-* make rules, verbose vs. silent:        --enable-silent-rules.
+* 'make' rules, verbose vs. silent:      '--enable-silent-rules'.
                                                               (line   6)
 * Makefile.am:                           Adding a new program module.
                                                               (line  46)
-* mf-nowin:                              Configure options for texk/web2c.
+* mf-nowin:                              Configure options for 'texk/web2c'.
                                                               (line  34)
-* mingw32:                               Cross configuring.   (line  27)
+* 'mingw32':                             Cross configuring.   (line  27)
 * MINGW32, Automake conditional:         Macros for Windows.  (line  20)
-* mktex.ac:                              kpathsea library.    (line  18)
-* mktextfm:                              kpathsea library.    (line  18)
+* mktex.ac:                              'kpathsea' library.  (line  18)
+* mktextfm:                              'kpathsea' library.  (line  18)
 * modules, for libraries:                Library modules.     (line   6)
 * modules, for programs:                 Program modules.     (line   6)
-* motif:                                 Configure options for texk/xdvik.
+* motif:                                 Configure options for 'texk/xdvik'.
                                                               (line   9)
 * native cross compilation:              Cross compilation.   (line  10)
 * NEWPROG-SRC, original source subdirectory: Adding a new program module.
@@ -5892,34 +5897,36 @@ Index
 * OBJCXX=OBJC-COMPILER:                  Build one package.   (line  77)
 * one engine, building:                  Build one engine.    (line   6)
 * one package, building:                 Build one package.   (line   6)
-* OpenGL, required for Asymptote:        asymptote.           (line   6)
+* OpenGL, required for Asymptote:        'asymptote'.         (line   6)
 * operating system distribution, building for: Distro builds. (line   6)
 * otangle:                               Cross problems.      (line  26)
 * overall build process:                 Building.            (line   6)
 * parallel build:                        Build in parallel.   (line   6)
 * paths, for installation:               Installation directories.
                                                               (line   6)
-* PDF files, size of:                    --disable-largefile. (line  11)
+* PDF files, size of:                    '--disable-largefile'.
+                                                              (line  11)
 * PDFLATEX:                              Variables for configure.
                                                               (line  41)
 * PERL:                                  Variables for configure.
                                                               (line  39)
-* perl, required by web2c, etc.:         Prerequisites.       (line  29)
+* 'perl', required by 'web2c', etc.:     Prerequisites.       (line  29)
 * PKG_CONFIG:                            Variables for configure.
                                                               (line  23)
 * plain.tex, not in source tree:         Installing.          (line   8)
-* png library:                           png library.         (line   6)
-* PostScript files, size of:             --disable-largefile. (line  11)
+* png library:                           'png' library.       (line   6)
+* PostScript files, size of:             '--disable-largefile'.
+                                                              (line  11)
 * Preining, Norbert:                     Distro builds.       (line  54)
-* preloaded binaries:                    Configure options for texk/web2c.
+* preloaded binaries:                    Configure options for 'texk/web2c'.
                                                               (line  45)
 * prerequisites for building:            Prerequisites.       (line   6)
 * program module, adding:                Adding a new program module.
                                                               (line   6)
 * program modules:                       Program modules.     (line   6)
-* program-specific configure options:    Program-specific configure options.
+* program-specific 'configure' options:  Program-specific configure options.
                                                               (line   6)
-* proxy build system:                    png library.         (line  35)
+* proxy build system:                    'png' library.       (line  36)
 * Python, required by ICU:               Prerequisites.       (line  23)
 * reautoconf:                            Build system tools.  (line  28)
 * reautoconf, for new program:           Adding a new program module.
@@ -5928,14 +5935,15 @@ Index
 * requirements for building:             Prerequisites.       (line   6)
 * runscript.exe:                         Macros for Windows.  (line  25)
 * scripts, linked and not maintained:    Linked scripts.      (line   6)
-* scrolling, smooth:                     Configure options for texk/xdvik.
+* scrolling, smooth:                     Configure options for 'texk/xdvik'.
                                                               (line  13)
 * SED:                                   Variables for configure.
                                                               (line  34)
 * setup macros, general:                 General setup macros.
                                                               (line   6)
 * shared libraries, using vs. avoiding:  Distro builds.       (line  11)
-* size of PDF and PS files:              --disable-largefile. (line  11)
+* size of PDF and PS files:              '--disable-largefile'.
+                                                              (line  11)
 * size of source tree:                   Build one package.   (line  63)
 * source code declarations:              Declarations and definitions.
                                                               (line   6)
@@ -5943,8 +5951,8 @@ Index
 * source tree:                           Layout and infrastructure.
                                                               (line   6)
 * squeeze:                               Cross problems.      (line  13)
-* squeeze/configure.ac:                  xdvik package.       (line  14)
-* static functions:                      Declarations and definitions.
+* squeeze/configure.ac:                  'xdvik' package.     (line  14)
+* 'static' functions:                    Declarations and definitions.
                                                               (line  35)
 * static linking for C++:                Macros for compilers.
                                                               (line  29)
@@ -5953,12 +5961,12 @@ Index
 * Subversion repository:                 Build system tools.  (line  38)
 * support files, separate from build:    Installing.          (line   8)
 * symlinks, used for scripts:            Linked scripts.      (line   6)
-* synctex:                               Configure options for texk/web2c.
+* synctex:                               Configure options for 'texk/web2c'.
                                                               (line  59)
-* synctex <1>:                           Configure options for texk/web2c.
+* synctex <1>:                           Configure options for 'texk/web2c'.
                                                               (line  64)
 * system distribution, building for:     Distro builds.       (line   6)
-* t1utils package:                       t1utils package.     (line   6)
+* t1utils package:                       't1utils' package.   (line   6)
 * tangle:                                Cross problems.      (line  26)
 * tests, running:                        Overview of build system.
                                                               (line   6)
@@ -5985,13 +5993,13 @@ Index
 * use-commit-times, Subversion:          Build system tools.  (line  38)
 * variable declarations, in source code: Declarations and definitions.
                                                               (line  52)
-* variables for configure:               Variables for configure.
+* variables for 'configure':             Variables for configure.
                                                               (line   6)
 * warning, discards qualifiers:          Const.               (line  30)
 * WARNING_C[XX]FLAGS:                    Macros for compilers.
                                                               (line   9)
 * web2c program:                         Cross problems.      (line  13)
-* web2c.ac:                              Configure options for texk/web2c.
+* web2c.ac:                              Configure options for 'texk/web2c'.
                                                               (line  37)
 * wget:                                  Linked scripts.      (line  23)
 * WIN32, Automake conditional:           Macros for Windows.  (line  16)
@@ -6000,438 +6008,433 @@ Index
 * Windows, macros for:                   Macros for Windows.  (line   6)
 * withenable.ac, for new modules:        Adding a new program module.
                                                               (line  14)
-* Work/ top-level directory:             Top-level directories.
+* 'Work/' top-level directory:           Top-level directories.
                                                               (line  39)
 * wrapper binary for scripts on Windows: Linked scripts.      (line   6)
-* wrapper build system:                  freetype library.    (line   6)
-* X toolkit:                             Configure options for texk/web2c.
+* wrapper build system:                  'freetype' library.  (line   6)
+* X toolkit:                             Configure options for 'texk/web2c'.
                                                               (line  22)
 * X11 development, required by X clients: Prerequisites.      (line  36)
-* X11 headers, and const:                Const.               (line  21)
-* xasy:                                  asymptote.           (line   6)
-* xaw:                                   Configure options for texk/xdvik.
+* X11 headers, and 'const':              Const.               (line  21)
+* xasy:                                  'asymptote'.         (line   6)
+* xaw:                                   Configure options for 'texk/xdvik'.
                                                               (line   9)
-* xdvik:                                 xdvik package.       (line   6)
-* xdvik <1>:                             Configure options for texk/xdvik.
+* xdvik:                                 'xdvik' package.     (line   6)
+* xdvik <1>:                             Configure options for 'texk/xdvik'.
                                                               (line   6)
-* xdvipdfmx:                             Configure options for texk/dvipdfm-x.
+* xdvipdfmx:                             Configure options for 'texk/dvipdfm-x'.
                                                               (line   6)
 * xindy:                                 Linked scripts.      (line  23)
-* xindy <1>:                             xindy package.       (line   6)
-* xindy <2>:                             Configure options for utils/xindy.
+* xindy <1>:                             'xindy' package.     (line   6)
+* xindy <2>:                             Configure options for 'utils/xindy'.
                                                               (line   6)
-* xindy cross compiling requires clisp:  Cross problems.      (line  33)
-* XInput:                                Configure options for texk/xdvik.
+* 'xindy' cross compiling requires 'clisp': Cross problems.   (line  33)
+* XInput:                                Configure options for 'texk/xdvik'.
                                                               (line  13)
-* Xlib:                                  Configure options for texk/web2c.
+* Xlib:                                  Configure options for 'texk/web2c'.
                                                               (line  22)
 * xz:                                    Linked scripts.      (line  23)
-* zlib library:                          zlib library.        (line   6)
+* zlib library:                          'zlib' library.      (line   6)
 
 
 
 Tag Table:
 Node: Top1208
 Node: Introduction2118
-Node: Overview of build system3943
-Node: Prerequisites5994
-Ref: Prerequisites-Footnote-18993
-Node: Building9296
-Node: Build iteration10638
-Node: Build in parallel11682
-Node: Build distribution12287
-Node: Build one package12935
-Node: Build one engine17647
-Node: Cross compilation20072
-Node: Cross configuring21351
-Node: Cross problems23028
-Node: Installing24690
-Node: Installation directories25710
-Node: Linked scripts27528
-Node: Distro builds29019
-Node: Layout and infrastructure31395
-Node: Build system tools32226
-Node: Top-level directories34443
-Node: Autoconf macros36679
-Node: General setup macros37441
-Node: Macros for programs38316
-Node: Macros for compilers39117
-Node: Macros for libraries40525
-Node: Macros for library and header flags40951
-Node: Macros for Windows42862
-Node: Library modules44449
-Node: png library44938
-Node: zlib library47286
-Node: freetype library47801
-Node: kpathsea library48497
-Node: Program modules49876
-Node: t1utils package50304
-Node: xindy package50849
-Node: xdvik package51965
-Node: asymptote53024
-Node: Extending TeX Live53528
-Node: Adding a new program module54335
-Node: Adding a new engine59098
-Node: Adding a new generic library module60883
-Node: Adding a new TeX-specific library module63104
-Node: Configure options63802
-Node: Global configure options65183
-Node: --disable-native-texlive-build65725
-Node: --prefix --bindir ...66687
-Node: --disable-largefile67227
-Node: --disable-missing67769
-Node: --enable-compiler-warnings=LEVEL68170
-Node: --enable-cxx-runtime-hack68826
-Node: --enable-maintainer-mode69246
-Node: --enable-multiplatform69775
-Node: --enable-shared70348
-Node: --enable-silent-rules70719
-Node: --without-ln-s71171
-Node: --without-x71518
-Node: Program-specific configure options71706
-Node: --enable-PROG --disable-PROG72349
-Node: --disable-all-pkgs72622
-Node: Configure options for texk/web2c73405
-Node: Configure options for texk/bibtex-x75943
-Node: Configure options for texk/dvipdfm-x76486
-Node: Configure options for texk/dvisvgm77262
-Node: Configure options for texk/texlive78143
-Node: Configure options for texk/xdvik78564
-Node: Configure options for utils/xindy79185
-Node: Library-specific configure options80075
-Node: Configure options for kpathsea81036
-Node: Variables for configure81740
-Node: Coding conventions83166
-Node: Declarations and definitions83881
-Node: Const86055
-Node: Continuous integration87919
-Node: Transfer from Subversion to Github88583
-Node: Automatic update of the Git mirror90745
-Node: CI testing on Travis-CI91327
-Node: Releases on Github92036
-Node: install-tl92476
-Node: install-tl NAME92845
-Node: install-tl SYNOPSIS93003
-Node: install-tl DESCRIPTION93219
-Node: install-tl REFERENCES94279
-Node: install-tl OPTIONS94803
-Ref: install-tl *-gui* [[=]_module_]95144
-Ref: install-tl text95352
-Ref: install-tl tcl (or "perltk" or "wizard" or "expert" or nothing)95537
-Ref: install-tl *-no-gui*96169
-Ref: install-tl *-lang* _llcode_96259
-Ref: install-tl *-repository* _url|path_96883
-Ref: install-tl *-select-repository*97772
-Ref: install-tl *-all-options*98190
-Ref: install-tl *-custom-bin* _path_98445
-Ref: install-tl *-debug-fakenet*99275
-Ref: install-tl *-debug-translation*99451
-Ref: install-tl *-force-platform* _platform_99727
-Ref: install-tl *-help*, *--help*, *-?*99971
-Ref: install-tl *-in-place*100384
-Ref: install-tl *-init-from-profile* _profile_file_100929
-Ref: install-tl *-logfile* _file_101198
-Ref: install-tl *-no-cls*101549
-Ref: install-tl *-no-persistent-downloads*101697
-Ref: install-tl *-persistent-downloads*101722
-Ref: install-tl *-no-verify-downloads*102340
-Ref: install-tl *-non-admin*102703
-Ref: install-tl *-portable*102796
-Ref: install-tl *-print-platform*102935
-Ref: install-tl *-profile* _profile_file_103133
-Ref: install-tl *-q*103354
-Ref: install-tl *-scheme* _scheme_103416
-Ref: install-tl *-v*103890
-Ref: install-tl *-version*, *--version*104045
-Node: install-tl PROFILES104179
-Ref: install-tl instopt_adjustpath (default 0 on Unix, 1 on Windows)107045
-Ref: install-tl instopt_adjustrepo (default 1)107121
-Ref: install-tl instopt_letter (default 0)107258
-Ref: install-tl instopt_portable (default 0)107349
-Ref: install-tl instopt_write18_restricted (default 1)107445
-Node: install-tl ENVIRONMENT VARIABLES108784
-Ref: install-tl TEXLIVE_DOWNLOADER109162
-Ref: install-tl TL_DOWNLOAD_PROGRAM109185
-Ref: install-tl TL_DOWNLOAD_ARGS109205
-Ref: install-tl TEXLIVE_INSTALL_ENV_NOCHECK109409
-Ref: install-tl TEXLIVE_INSTALL_NO_CONTEXT_CACHE109611
-Ref: install-tl TEXLIVE_INSTALL_NO_RESUME109720
-Ref: install-tl TEXLIVE_INSTALL_NO_WELCOME109872
-Ref: install-tl TEXLIVE_INSTALL_PAPER109993
-Ref: install-tl TEXLIVE_INSTALL_PREFIX110139
-Ref: install-tl TEXLIVE_INSTALL_TEXMFCONFIG110170
-Ref: install-tl TEXLIVE_INSTALL_TEXMFVAR110198
-Ref: install-tl TEXLIVE_INSTALL_TEXMFHOME110227
-Ref: install-tl TEXLIVE_INSTALL_TEXMFLOCAL110257
-Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSCONFIG110291
-Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSVAR110322
-Ref: install-tl NOPERLDOC110737
-Node: install-tl AUTHORS AND COPYRIGHT110801
-Node: tlmgr111214
-Node: tlmgr NAME111699
-Node: tlmgr SYNOPSIS111831
-Node: tlmgr DESCRIPTION112021
-Node: tlmgr EXAMPLES113120
-Ref: tlmgr tlmgr option repository ctan113371
-Ref: tlmgr tlmgr option repository https://mirror.ctan.org/systems/texlive/tlnet113444
-Ref: tlmgr tlmgr update --list114049
-Ref: tlmgr tlmgr update --all114142
-Ref: tlmgr tlmgr info _what_114299
-Node: tlmgr OPTIONS114561
-Ref: tlmgr *--repository* _url|path_115081
-Ref: tlmgr /some/local/dir116267
-Ref: tlmgr file:/some/local/dir116296
-Ref: tlmgr ctan116369
-Ref: tlmgr https://mirror.ctan.org/systems/texlive/tlnet116423
-Ref: tlmgr http://server/path/to/tlnet116764
-Ref: tlmgr https://server/path/to/tlnet117145
-Ref: tlmgr ftp://server/path/to/tlnet117613
-Ref: tlmgr user@machine:/path/to/tlnet117745
-Ref: tlmgr scp://user@machine/path/to/tlnet117786
-Ref: tlmgr ssh://user@machine/path/to/tlnet117827
-Ref: tlmgr *--gui* [_action_]118220
-Ref: tlmgr *--gui-lang* _llcode_119033
-Ref: tlmgr *--command-logfile* _file_119774
-Ref: tlmgr *--debug-translation*120040
-Ref: tlmgr *--machine-readable*120243
-Ref: tlmgr *--no-execute-actions*120511
-Ref: tlmgr *--package-logfile* _file_120704
-Ref: tlmgr *--pause*120958
-Ref: tlmgr *--persistent-downloads*121113
-Ref: tlmgr *--no-persistent-downloads*121141
-Ref: tlmgr *--pin-file*121635
-Ref: tlmgr *--usermode*121853
-Ref: tlmgr *--usertree* _dir_121973
-Ref: tlmgr *--verify-repo=[none|main|all]*122099
-Node: tlmgr ACTIONS122998
-Node: tlmgr help123859
-Node: tlmgr version124336
-Node: tlmgr backup124599
-Ref: tlmgr *backup [_option_...] --all*124770
-Ref: tlmgr *backup [_option_...] _pkg_...*124803
-Ref: tlmgr *--backupdir* _directory_125869
-Ref: tlmgr *--all*126086
-Ref: tlmgr *--clean*[=_N_]126338
-Ref: tlmgr *--dry-run*126665
-Node: tlmgr candidates _pkg_126795
-Node: tlmgr check [_option_...] [depends|executes|files|runfiles|texmfdbs|all]127150
-Ref: tlmgr *depends*127664
-Ref: tlmgr *executes*128006
-Ref: tlmgr *files*128121
-Ref: tlmgr *runfiles*128257
-Ref: tlmgr *texmfdbs*128394
-Ref: tlmgr - all items in TEXMFDBS have the !! prefix.128624
-Ref: tlmgr - all items in TEXMFBDS have an ls-R file (if they exist at all).128700
-Ref: tlmgr - all items in TEXMF with !! are listed in TEXMFDBS.128765
-Ref: tlmgr - all items in TEXMF with an ls-R file are listed in TEXMFDBS.128840
-Ref: tlmgr *--use-svn*128865
-Node: tlmgr conf129006
-Ref: tlmgr *conf [texmf|tlmgr|updmap [--conffile _file_] [--delete] [_key_ [_value_]]]*129294
-Ref: tlmgr *conf auxtrees [--conffile _file_] [show|add|remove] [_value_]*129358
-Node: tlmgr dump-tlpdb [_option_...] [--json]131773
-Ref: tlmgr *--local*132206
-Ref: tlmgr *--remote*132245
-Ref: tlmgr *--json*132283
-Node: tlmgr generate132854
-Ref: tlmgr *generate [_option_...] language*133050
-Ref: tlmgr *generate [_option_...] language.dat*133089
-Ref: tlmgr *generate [_option_...] language.def*133128
-Ref: tlmgr *generate [_option_...] language.dat.lua*133171
-Ref: tlmgr *--dest* _output_file_135498
-Ref: tlmgr *--localcfg* _local_conf_file_136074
-Ref: tlmgr *--rebuild-sys*136197
-Node: tlmgr gui137012
-Node: tlmgr info137190
-Ref: tlmgr *info [_option_...] _pkg_...*137352
-Ref: tlmgr *info [_option_...] collections*137386
-Ref: tlmgr *info [_option_...] schemes*137416
-Ref: tlmgr *--list*138946
-Ref: tlmgr *--only-installed*139260
-Ref: tlmgr *--only-remote*139448
-Ref: tlmgr *--data item1,item2,...*139752
-Ref: tlmgr *--json* 1141124
-Node: tlmgr init-usertree141507
-Node: tlmgr install [_option_...] _pkg_...141888
-Ref: tlmgr *--dry-run* 1142396
-Ref: tlmgr *--file*142513
-Ref: tlmgr *--force*142735
-Ref: tlmgr *--no-depends*142955
-Ref: tlmgr *--no-depends-at-all*143114
-Ref: tlmgr *--reinstall*143514
-Ref: tlmgr *--with-doc*143892
-Ref: tlmgr *--with-src*143905
-Node: tlmgr key144633
-Ref: tlmgr *key list*144791
-Ref: tlmgr *key add _file_*144809
-Ref: tlmgr *key remove _keyid_*144831
-Node: tlmgr list145425
-Node: tlmgr option145587
-Ref: tlmgr *option [--json] [show]*145742
-Ref: tlmgr *option [--json] showall|help*145773
-Ref: tlmgr *option _key_ [_value_]*145799
-Node: tlmgr paper150378
-Ref: tlmgr *paper [a4|letter]*150527
-Ref: tlmgr *<[xdvi|pdftex|dvips|dvipdfmx|context|psutils] paper [_papersize_|--list]*>150603
-Ref: tlmgr *paper --json*150618
-Node: tlmgr path151833
-Ref: tlmgr *path [--w32mode=user|admin] add*151994
-Ref: tlmgr *path [--w32mode=user|admin] remove*152031
-Node: tlmgr pinning153516
-Ref: tlmgr pinning show153757
-Ref: tlmgr pinning add _repo_ _pkgglob_...153830
-Ref: tlmgr pinning remove _repo_ _pkgglob_...153949
-Ref: tlmgr pinning remove _repo_ --all154102
-Node: tlmgr platform154156
-Ref: tlmgr *platform list|add|remove _platform_...*154342
-Ref: tlmgr *platform set _platform_*154369
-Ref: tlmgr *platform set auto*154390
-Ref: tlmgr *--dry-run* 2155267
-Node: tlmgr postaction155386
-Ref: tlmgr *postaction [_option_...] install [shortcut|fileassoc|script] [_pkg_...]*155616
-Ref: tlmgr *postaction [_option_...] remove [shortcut|fileassoc|script] [_pkg_...]*155690
-Ref: tlmgr *--w32mode=[user|admin]*156005
-Ref: tlmgr *--fileassocmode=[1|2]*156421
-Ref: tlmgr *--all* 1156706
-Node: tlmgr print-platform156761
-Node: tlmgr print-platform-info157092
-Node: tlmgr remove [_option_...] _pkg_...157392
-Ref: tlmgr *--all* 2157876
-Ref: tlmgr *--backup*157986
-Ref: tlmgr *--backupdir* _directory_ 1158012
-Ref: tlmgr *--no-depends* 1158417
-Ref: tlmgr *--no-depends-at-all* 1158479
-Ref: tlmgr *--force* 1158582
-Ref: tlmgr *--dry-run* 3159055
-Node: tlmgr repository159432
-Ref: tlmgr *repository list*159620
-Ref: tlmgr *repository list _path|url|tag_*159654
-Ref: tlmgr *repository add _path_ [_tag_]*159687
-Ref: tlmgr *repository remove _path|tag_*159719
-Ref: tlmgr *repository set _path_[#_tag_] [_path_[#_tag_] ...]*159773
-Ref: tlmgr *repository status*159794
-Ref: tlmgr The tag (which can be the same as the url);161021
-Node: tlmgr restore161499
-Ref: tlmgr *restore [_option_...] _pkg_ [_rev_]*161678
-Ref: tlmgr *restore [_option_...] --all*161708
-Ref: tlmgr *--all* 3162408
-Ref: tlmgr *--backupdir* _directory_ 2162622
-Ref: tlmgr *--dry-run* 4162803
-Ref: tlmgr *--force* 2162935
-Ref: tlmgr *--json* 2162981
-Node: tlmgr search163308
-Ref: tlmgr *search [_option_...] _what_*163472
-Ref: tlmgr *search [_option_...] --file _what_*163509
-Ref: tlmgr *search [_option_...] --all _what_*163545
-Ref: tlmgr *--file* 1163765
-Ref: tlmgr *--all* 4163827
-Ref: tlmgr *--global*163916
-Ref: tlmgr *--word*164043
-Node: tlmgr shell164358
-Ref: tlmgr protocol165093
-Ref: tlmgr help 1165157
-Ref: tlmgr version 1165210
-Ref: tlmgr quit, end, bye, byebye, EOF165278
-Ref: tlmgr restart165299
-Ref: tlmgr load [local|remote]165422
-Ref: tlmgr save165492
-Ref: tlmgr get [_var_] =item set [_var_ [_val_]]165615
-Node: tlmgr show166216
-Node: tlmgr uninstall166383
-Node: tlmgr update [_option_...] [_pkg_...]166613
-Ref: tlmgr *--all* 5166984
-Ref: tlmgr *--self*169163
-Ref: tlmgr *--dry-run* 5169927
-Ref: tlmgr *--list* [_pkg_]170104
-Ref: tlmgr *--exclude* _pkg_170793
-Ref: tlmgr *--no-auto-remove* [_pkg_...]171593
-Ref: tlmgr *--no-auto-install* [_pkg_...]172077
-Ref: tlmgr *--reinstall-forcibly-removed*172839
-Ref: tlmgr *--backup* 1173374
-Ref: tlmgr *--backupdir* _directory_ 3173400
-Ref: tlmgr *--no-depends* 2174566
-Ref: tlmgr *--no-depends-at-all* 2174769
-Ref: tlmgr *--force* 3174872
-Node: tlmgr CONFIGURATION FILE FOR TLMGR175863
-Ref: tlmgr auto-remove = 0 or 1 (default 1), same as command-line option.176865
-Ref: tlmgr gui-expertmode = 0 or 1 (default 1). This switches between the full GUI and a simplified GUI with only the most common settings.176997
-Ref: tlmgr gui-lang = _llcode_, with a language code value as with the command-line option.177081
-Ref: tlmgr no-checksums = 0 or 1 (default 0, see below).177130
-Ref: tlmgr persistent-downloads = 0 or 1 (default 1), same as command-line option.177205
-Ref: tlmgr require-verification = 0 or 1 (default 0), same as command-line option.177280
-Ref: tlmgr tkfontscale = _floating-point number_ (default 1.0); scaling factor for fonts in the Tk-based frontends.177388
-Ref: tlmgr update-exclude = _comma-separated list of packages_ (no spaces allowed). Same as the command line option --exclude for the update action.177533
-Ref: tlmgr verify-downloads = 0 or 1 (default 1), same as command-line option.177604
-Ref: tlmgr allowed-actions = _action1_[,_action2_,...] The value is a comma-separated list (no spaces) of tlmgr actions which are allowed to be executed when tlmgr is invoked in system mode (that is, without --usermode). This allows distributors to include tlmgr in their packaging, but allow only a restricted set of actions that do not interfere with their distro package manager. For native TeX Live installations, it doesn't make sense to set this.178121
-Node: tlmgr CRYPTOGRAPHIC VERIFICATION178953
-Node: tlmgr Configuration of GnuPG invocation181126
-Node: tlmgr USER MODE181764
-Node: tlmgr User mode install184610
-Node: tlmgr User mode backup, restore, remove, update185754
-Node: tlmgr User mode generate, option, paper186196
-Node: tlmgr MULTIPLE REPOSITORIES186530
-Node: tlmgr Pinning188259
-Node: tlmgr GUI FOR TLMGR190182
-Node: tlmgr Main display191831
-Node: tlmgr Display configuration area192083
-Ref: tlmgr Status192444
-Ref: tlmgr Category192608
-Ref: tlmgr Match192794
-Ref: tlmgr Selection192975
-Ref: tlmgr Display configuration buttons193179
-Node: tlmgr Package list area193362
-Ref: tlmgr a checkbox193946
-Ref: tlmgr package name194082
-Ref: tlmgr local revision (and version)194181
-Ref: tlmgr remote revision (and version)194556
-Ref: tlmgr short description194853
-Node: tlmgr Main display action buttons194898
-Ref: tlmgr Update all installed195164
-Ref: tlmgr Update195536
-Ref: tlmgr Install195586
-Ref: tlmgr Remove195772
-Ref: tlmgr Backup195950
-Node: tlmgr Menu bar196107
-Ref: tlmgr tlmgr menu196330
-Ref: tlmgr Options menu196638
-Ref: tlmgr Actions menu197721
-Ref: tlmgr Help menu198149
-Node: tlmgr GUI options198283
-Ref: tlmgr -background _color_198529
-Ref: tlmgr -font " _fontname_ _fontsize_ "198594
-Ref: tlmgr -foreground _color_198752
-Ref: tlmgr -geometry _geomspec_198804
-Ref: tlmgr -xrm _xresource_198996
-Node: tlmgr MACHINE-READABLE OUTPUT199265
-Node: tlmgr Machine-readable update and install output200075
-Ref: tlmgr location-url _location_201351
-Ref: tlmgr total-bytes _count_201567
-Ref: tlmgr _pkgname_201977
-Ref: tlmgr _status_202187
-Ref: tlmgr d202265
-Ref: tlmgr f202325
-Ref: tlmgr u202504
-Ref: tlmgr r202550
-Ref: tlmgr a202673
-Ref: tlmgr i202851
-Ref: tlmgr I202970
-Ref: tlmgr _localrev_203072
-Ref: tlmgr _serverrev_203179
-Ref: tlmgr _size_203291
-Ref: tlmgr _runtime_203460
-Ref: tlmgr _esttot_203530
-Node: tlmgr Machine-readable option output203563
-Node: tlmgr ENVIRONMENT VARIABLES204075
-Ref: tlmgr TEXLIVE_COMPRESSOR204586
-Ref: tlmgr TEXLIVE_DOWNLOADER205434
-Ref: tlmgr TL_DOWNLOAD_PROGRAM205457
-Ref: tlmgr TL_DOWNLOAD_ARGS205477
-Ref: tlmgr TEXLIVE_PREFER_OWN206503
-Node: tlmgr AUTHORS AND COPYRIGHT207327
-Node: Index207729
+Node: Overview of build system3941
+Node: Prerequisites5992
+Ref: Prerequisites-Footnote-18991
+Node: Building9294
+Node: Build iteration10636
+Node: Build in parallel11680
+Node: Build distribution12285
+Node: Build one package12933
+Node: Build one engine17648
+Node: Cross compilation20073
+Node: Cross configuring21352
+Node: Cross problems23029
+Node: Installing24691
+Node: Installation directories25711
+Node: Linked scripts27529
+Node: Distro builds29020
+Node: Layout and infrastructure31396
+Node: Build system tools32227
+Node: Top-level directories34444
+Node: Autoconf macros36680
+Node: General setup macros37442
+Node: Macros for programs38317
+Node: Macros for compilers39118
+Node: Macros for libraries40526
+Node: Macros for library and header flags40952
+Node: Macros for Windows42863
+Node: Library modules44450
+Node: 'png' library44947
+Node: 'zlib' library47299
+Node: 'freetype' library47820
+Node: 'kpathsea' library48522
+Node: Program modules49905
+Node: 't1utils' package50341
+Node: 'xindy' package50890
+Node: 'xdvik' package52012
+Node: 'asymptote'53077
+Node: Extending TeX Live53585
+Node: Adding a new program module54392
+Node: Adding a new engine59155
+Node: Adding a new generic library module60940
+Node: Adding a new TeX-specific library module63161
+Node: Configure options63859
+Node: Global configure options65240
+Node: '--disable-native-texlive-build'65808
+Node: '--prefix' '--bindir' ...66776
+Node: '--disable-largefile'67324
+Node: '--disable-missing'67874
+Node: '--enable-compiler-warnings='LEVEL68281
+Node: '--enable-cxx-runtime-hack'68943
+Node: '--enable-maintainer-mode'69369
+Node: '--enable-multiplatform'69904
+Node: '--enable-shared'70483
+Node: '--enable-silent-rules'70860
+Node: '--without-ln-s'71318
+Node: '--without-x'71671
+Node: Program-specific configure options71863
+Node: '--enable-PROG' '--disable-PROG'72526
+Node: '--disable-all-pkgs'72805
+Node: Configure options for 'texk/web2c'73596
+Node: Configure options for 'texk/bibtex-x'76140
+Node: Configure options for 'texk/dvipdfm-x'76689
+Node: Configure options for 'texk/dvisvgm'77471
+Node: Configure options for 'texk/texlive'78358
+Node: Configure options for 'texk/xdvik'78785
+Node: Configure options for 'utils/xindy'79412
+Node: Library-specific configure options80306
+Node: Configure options for 'kpathsea'81269
+Node: Variables for configure81977
+Node: Coding conventions83403
+Node: Declarations and definitions84118
+Node: Const86292
+Node: Continuous integration88156
+Node: Transfer from Subversion to Github88820
+Node: Automatic update of the Git mirror90982
+Node: CI testing on Travis-CI91564
+Node: Releases on Github92273
+Node: install-tl92713
+Node: install-tl NAME93082
+Node: install-tl SYNOPSIS93240
+Node: install-tl DESCRIPTION93456
+Node: install-tl REFERENCES94516
+Node: install-tl OPTIONS95040
+Ref: install-tl *-gui* [[=]_module_]95381
+Ref: install-tl 'text'95589
+Ref: install-tl 'tcl' (or "perltk" or "wizard" or "expert" or nothing)95774
+Ref: install-tl *-no-gui*96406
+Ref: install-tl *-lang* _llcode_96496
+Ref: install-tl *-repository* _url|path_97120
+Ref: install-tl *-select-repository*98009
+Ref: install-tl *-all-options*98427
+Ref: install-tl *-custom-bin* _path_98682
+Ref: install-tl *-debug-fakenet*99512
+Ref: install-tl *-debug-translation*99688
+Ref: install-tl *-force-platform* _platform_99964
+Ref: install-tl *-help*, *--help*, *-?*100208
+Ref: install-tl *-in-place*100621
+Ref: install-tl *-init-from-profile* _profile_file_101166
+Ref: install-tl *-logfile* _file_101435
+Ref: install-tl *-no-cls*101786
+Ref: install-tl *-no-persistent-downloads*101934
+Ref: install-tl *-persistent-downloads*101959
+Ref: install-tl *-no-verify-downloads*102577
+Ref: install-tl *-non-admin*102940
+Ref: install-tl *-portable*103033
+Ref: install-tl *-print-platform*103172
+Ref: install-tl *-profile* _profile_file_103370
+Ref: install-tl *-q*103591
+Ref: install-tl *-scheme* _scheme_103653
+Ref: install-tl *-v*104127
+Ref: install-tl *-version*, *--version*104282
+Node: install-tl PROFILES104416
+Ref: install-tl 'instopt_adjustpath' (default 0 on Unix, 1 on Windows)107282
+Ref: install-tl 'instopt_adjustrepo' (default 1)107358
+Ref: install-tl 'instopt_letter' (default 0)107495
+Ref: install-tl 'instopt_portable' (default 0)107586
+Ref: install-tl 'instopt_write18_restricted' (default 1)107682
+Node: install-tl ENVIRONMENT VARIABLES109021
+Ref: install-tl 'TEXLIVE_DOWNLOADER'109399
+Ref: install-tl 'TL_DOWNLOAD_PROGRAM'109422
+Ref: install-tl 'TL_DOWNLOAD_ARGS'109442
+Ref: install-tl 'TEXLIVE_INSTALL_ENV_NOCHECK'109646
+Ref: install-tl 'TEXLIVE_INSTALL_NO_CONTEXT_CACHE'109848
+Ref: install-tl 'TEXLIVE_INSTALL_NO_RESUME'109957
+Ref: install-tl 'TEXLIVE_INSTALL_NO_WELCOME'110109
+Ref: install-tl 'TEXLIVE_INSTALL_PAPER'110230
+Ref: install-tl 'TEXLIVE_INSTALL_PREFIX'110376
+Ref: install-tl 'TEXLIVE_INSTALL_TEXMFCONFIG'110407
+Ref: install-tl 'TEXLIVE_INSTALL_TEXMFVAR'110435
+Ref: install-tl 'TEXLIVE_INSTALL_TEXMFHOME'110464
+Ref: install-tl 'TEXLIVE_INSTALL_TEXMFLOCAL'110494
+Ref: install-tl 'TEXLIVE_INSTALL_TEXMFSYSCONFIG'110528
+Ref: install-tl 'TEXLIVE_INSTALL_TEXMFSYSVAR'110559
+Ref: install-tl 'NOPERLDOC'110974
+Node: install-tl AUTHORS AND COPYRIGHT111038
+Node: tlmgr111451
+Node: tlmgr NAME111936
+Node: tlmgr SYNOPSIS112068
+Node: tlmgr DESCRIPTION112258
+Node: tlmgr EXAMPLES113357
+Ref: tlmgr 'tlmgr option repository ctan'113608
+Ref: tlmgr 'tlmgr option repository https://mirror.ctan.org/systems/texlive/tlnet'113681
+Ref: tlmgr 'tlmgr update --list'114286
+Ref: tlmgr 'tlmgr update --all'114379
+Ref: tlmgr 'tlmgr info' _what_114536
+Node: tlmgr OPTIONS114798
+Ref: tlmgr *--repository* _url|path_115318
+Ref: tlmgr '/some/local/dir'116504
+Ref: tlmgr 'file:/some/local/dir'116533
+Ref: tlmgr 'ctan'116606
+Ref: tlmgr 'https://mirror.ctan.org/systems/texlive/tlnet'116660
+Ref: tlmgr 'http://server/path/to/tlnet'117001
+Ref: tlmgr 'https://server/path/to/tlnet'117382
+Ref: tlmgr 'ftp://server/path/to/tlnet'117850
+Ref: tlmgr 'user@machine:/path/to/tlnet'117982
+Ref: tlmgr 'scp://user@machine/path/to/tlnet'118023
+Ref: tlmgr 'ssh://user@machine/path/to/tlnet'118064
+Ref: tlmgr *--gui* [_action_]118457
+Ref: tlmgr *--gui-lang* _llcode_119270
+Ref: tlmgr *--command-logfile* _file_120011
+Ref: tlmgr *--debug-translation*120277
+Ref: tlmgr *--machine-readable*120480
+Ref: tlmgr *--no-execute-actions*120748
+Ref: tlmgr *--package-logfile* _file_120941
+Ref: tlmgr *--pause*121195
+Ref: tlmgr *--persistent-downloads*121350
+Ref: tlmgr *--no-persistent-downloads*121378
+Ref: tlmgr *--pin-file*121872
+Ref: tlmgr *--usermode*122090
+Ref: tlmgr *--usertree* _dir_122210
+Ref: tlmgr *--verify-repo=[none|main|all]*122336
+Node: tlmgr ACTIONS123235
+Node: tlmgr help124096
+Node: tlmgr version124573
+Node: tlmgr backup124836
+Ref: tlmgr *backup [_option_...] --all*125007
+Ref: tlmgr *backup [_option_...] _pkg_...*125040
+Ref: tlmgr *--backupdir* _directory_126106
+Ref: tlmgr *--all*126323
+Ref: tlmgr *--clean*[=_N_]126575
+Ref: tlmgr *--dry-run*126902
+Node: tlmgr candidates _pkg_127032
+Node: tlmgr check [_option_...] [depends|executes|files|runfiles|texmfdbs|all]127387
+Ref: tlmgr *depends*127901
+Ref: tlmgr *executes*128243
+Ref: tlmgr *files*128358
+Ref: tlmgr *runfiles*128494
+Ref: tlmgr *texmfdbs*128631
+Ref: tlmgr - all items in 'TEXMFDBS' have the '!!' prefix.128861
+Ref: tlmgr - all items in 'TEXMFBDS' have an 'ls-R' file (if they exist at all).128937
+Ref: tlmgr - all items in 'TEXMF' with '!!' are listed in 'TEXMFDBS'.129002
+Ref: tlmgr - all items in 'TEXMF' with an 'ls-R' file are listed in 'TEXMFDBS'.129077
+Ref: tlmgr *--use-svn*129102
+Node: tlmgr conf129243
+Ref: tlmgr *conf [texmf|tlmgr|updmap [--conffile _file_] [--delete] [_key_ [_value_]]]*129531
+Ref: tlmgr *conf auxtrees [--conffile _file_] [show|add|remove] [_value_]*129595
+Node: tlmgr dump-tlpdb [_option_...] [--json]132010
+Ref: tlmgr *--local*132443
+Ref: tlmgr *--remote*132482
+Ref: tlmgr *--json*132520
+Node: tlmgr generate133091
+Ref: tlmgr *generate [_option_...] language*133287
+Ref: tlmgr *generate [_option_...] language.dat*133326
+Ref: tlmgr *generate [_option_...] language.def*133365
+Ref: tlmgr *generate [_option_...] language.dat.lua*133408
+Ref: tlmgr *--dest* _output_file_135735
+Ref: tlmgr *--localcfg* _local_conf_file_136311
+Ref: tlmgr *--rebuild-sys*136434
+Node: tlmgr gui137249
+Node: tlmgr info137427
+Ref: tlmgr *info [_option_...] _pkg_...*137589
+Ref: tlmgr *info [_option_...] collections*137623
+Ref: tlmgr *info [_option_...] schemes*137653
+Ref: tlmgr *--list*139183
+Ref: tlmgr *--only-installed*139497
+Ref: tlmgr *--only-remote*139685
+Ref: tlmgr *--data 'item1,item2,...'*139989
+Ref: tlmgr *--json* 1141361
+Node: tlmgr init-usertree141744
+Node: tlmgr install [_option_...] _pkg_...142125
+Ref: tlmgr *--dry-run* 1142633
+Ref: tlmgr *--file*142750
+Ref: tlmgr *--force*142972
+Ref: tlmgr *--no-depends*143192
+Ref: tlmgr *--no-depends-at-all*143351
+Ref: tlmgr *--reinstall*143751
+Ref: tlmgr *--with-doc*144129
+Ref: tlmgr *--with-src*144142
+Node: tlmgr key144870
+Ref: tlmgr *key list*145028
+Ref: tlmgr *key add _file_*145046
+Ref: tlmgr *key remove _keyid_*145068
+Node: tlmgr list145662
+Node: tlmgr option145824
+Ref: tlmgr *option [--json] [show]*145979
+Ref: tlmgr *option [--json] showall|help*146010
+Ref: tlmgr *option _key_ [_value_]*146036
+Node: tlmgr paper150615
+Ref: tlmgr *paper [a4|letter]*150764
+Ref: tlmgr *<[xdvi|pdftex|dvips|dvipdfmx|context|psutils] paper [_papersize_|--list]*>150840
+Ref: tlmgr *paper --json*150855
+Node: tlmgr path152070
+Ref: tlmgr *path [--w32mode=user|admin] add*152231
+Ref: tlmgr *path [--w32mode=user|admin] remove*152268
+Node: tlmgr pinning153753
+Ref: tlmgr 'pinning show'153994
+Ref: tlmgr 'pinning add' _repo_ _pkgglob_...154067
+Ref: tlmgr 'pinning remove' _repo_ _pkgglob_...154186
+Ref: tlmgr 'pinning remove _repo_ --all'154339
+Node: tlmgr platform154393
+Ref: tlmgr *platform list|add|remove _platform_...*154579
+Ref: tlmgr *platform set _platform_*154606
+Ref: tlmgr *platform set auto*154627
+Ref: tlmgr *--dry-run* 2155504
+Node: tlmgr postaction155623
+Ref: tlmgr *postaction [_option_...] install [shortcut|fileassoc|script] [_pkg_...]*155853
+Ref: tlmgr *postaction [_option_...] remove [shortcut|fileassoc|script] [_pkg_...]*155927
+Ref: tlmgr *--w32mode=[user|admin]*156242
+Ref: tlmgr *--fileassocmode=[1|2]*156658
+Ref: tlmgr *--all* 1156943
+Node: tlmgr print-platform156998
+Node: tlmgr print-platform-info157329
+Node: tlmgr remove [_option_...] _pkg_...157629
+Ref: tlmgr *--all* 2158113
+Ref: tlmgr *--backup*158223
+Ref: tlmgr *--backupdir* _directory_ 1158249
+Ref: tlmgr *--no-depends* 1158654
+Ref: tlmgr *--no-depends-at-all* 1158716
+Ref: tlmgr *--force* 1158819
+Ref: tlmgr *--dry-run* 3159292
+Node: tlmgr repository159669
+Ref: tlmgr *repository list*159857
+Ref: tlmgr *repository list _path|url|tag_*159891
+Ref: tlmgr *repository add _path_ [_tag_]*159924
+Ref: tlmgr *repository remove _path|tag_*159956
+Ref: tlmgr *repository set _path_[#_tag_] [_path_[#_tag_] ...]*160010
+Ref: tlmgr *repository status*160031
+Ref: tlmgr The tag (which can be the same as the url);161258
+Node: tlmgr restore161736
+Ref: tlmgr *restore [_option_...] _pkg_ [_rev_]*161915
+Ref: tlmgr *restore [_option_...] --all*161945
+Ref: tlmgr *--all* 3162645
+Ref: tlmgr *--backupdir* _directory_ 2162859
+Ref: tlmgr *--dry-run* 4163040
+Ref: tlmgr *--force* 2163172
+Ref: tlmgr *--json* 2163218
+Node: tlmgr search163545
+Ref: tlmgr *search [_option_...] _what_*163709
+Ref: tlmgr *search [_option_...] --file _what_*163746
+Ref: tlmgr *search [_option_...] --all _what_*163782
+Ref: tlmgr *--file* 1164002
+Ref: tlmgr *--all* 4164064
+Ref: tlmgr *--global*164153
+Ref: tlmgr *--word*164280
+Node: tlmgr shell164595
+Ref: tlmgr protocol165330
+Ref: tlmgr help 1165394
+Ref: tlmgr version 1165447
+Ref: tlmgr quit, end, bye, byebye, EOF165515
+Ref: tlmgr restart165536
+Ref: tlmgr load [local|remote]165659
+Ref: tlmgr save165729
+Ref: tlmgr get [_var_] =item set [_var_ [_val_]]165852
+Node: tlmgr show166453
+Node: tlmgr uninstall166620
+Node: tlmgr update [_option_...] [_pkg_...]166850
+Ref: tlmgr *--all* 5167221
+Ref: tlmgr *--self*169400
+Ref: tlmgr *--dry-run* 5170164
+Ref: tlmgr *--list* [_pkg_]170341
+Ref: tlmgr *--exclude* _pkg_171030
+Ref: tlmgr *--no-auto-remove* [_pkg_...]171830
+Ref: tlmgr *--no-auto-install* [_pkg_...]172314
+Ref: tlmgr *--reinstall-forcibly-removed*173076
+Ref: tlmgr *--backup* 1173611
+Ref: tlmgr *--backupdir* _directory_ 3173637
+Ref: tlmgr *--no-depends* 2174803
+Ref: tlmgr *--no-depends-at-all* 2175006
+Ref: tlmgr *--force* 3175109
+Node: tlmgr CONFIGURATION FILE FOR TLMGR176100
+Ref: tlmgr 'auto-remove =' 0 or 1 (default 1), same as command-line option.177102
+Ref: tlmgr 'gui-expertmode =' 0 or 1 (default 1). This switches between the full GUI and a simplified GUI with only the most common settings.177234
+Ref: tlmgr 'gui-lang =' _llcode_, with a language code value as with the command-line option.177318
+Ref: tlmgr 'no-checksums =' 0 or 1 (default 0, see below).177367
+Ref: tlmgr 'persistent-downloads =' 0 or 1 (default 1), same as command-line option.177442
+Ref: tlmgr 'require-verification =' 0 or 1 (default 0), same as command-line option.177517
+Ref: tlmgr 'tkfontscale =' _floating-point number_ (default 1.0); scaling factor for fonts in the Tk-based frontends.177625
+Ref: tlmgr 'update-exclude =' _comma-separated list of packages_ (no spaces allowed). Same as the command line option '--exclude' for the 'update' action.177770
+Ref: tlmgr 'verify-downloads =' 0 or 1 (default 1), same as command-line option.177841
+Ref: tlmgr 'allowed-actions =' _action1_[,_action2_,...] The value is a comma-separated list (no spaces) of 'tlmgr' actions which are allowed to be executed when 'tlmgr' is invoked in system mode (that is, without '--usermode'). This allows distributors to include 'tlmgr' in their packaging, but allow only a restricted set of actions that do not interfere with their distro package manager. For native TeX Live installations, it doesn't make sense to set this.178358
+Node: tlmgr CRYPTOGRAPHIC VERIFICATION179190
+Node: tlmgr Configuration of GnuPG invocation181363
+Node: tlmgr USER MODE182001
+Node: tlmgr User mode install184847
+Node: tlmgr User mode backup, restore, remove, update185991
+Node: tlmgr User mode generate, option, paper186433
+Node: tlmgr MULTIPLE REPOSITORIES186767
+Node: tlmgr Pinning188496
+Node: tlmgr GUI FOR TLMGR190419
+Node: tlmgr Main display192068
+Node: tlmgr Display configuration area192320
+Ref: tlmgr Status192681
+Ref: tlmgr Category192845
+Ref: tlmgr Match193031
+Ref: tlmgr Selection193212
+Ref: tlmgr Display configuration buttons193416
+Node: tlmgr Package list area193599
+Ref: tlmgr a checkbox194183
+Ref: tlmgr package name194319
+Ref: tlmgr local revision (and version)194418
+Ref: tlmgr remote revision (and version)194793
+Ref: tlmgr short description195090
+Node: tlmgr Main display action buttons195135
+Ref: tlmgr Update all installed195401
+Ref: tlmgr Update195773
+Ref: tlmgr Install195823
+Ref: tlmgr Remove196009
+Ref: tlmgr Backup196187
+Node: tlmgr Menu bar196344
+Ref: tlmgr 'tlmgr' menu196567
+Ref: tlmgr 'Options menu'196875
+Ref: tlmgr 'Actions menu'197958
+Ref: tlmgr 'Help menu'198386
+Node: tlmgr GUI options198520
+Ref: tlmgr '-background' _color_198766
+Ref: tlmgr '-font "' _fontname_ _fontsize_ '"'198831
+Ref: tlmgr '-foreground' _color_198989
+Ref: tlmgr '-geometry' _geomspec_199041
+Ref: tlmgr '-xrm' _xresource_199233
+Node: tlmgr MACHINE-READABLE OUTPUT199502
+Node: tlmgr Machine-readable 'update' and 'install' output200318
+Ref: tlmgr 'location-url' _location_201600
+Ref: tlmgr 'total-bytes' _count_201816
+Ref: tlmgr _pkgname_202226
+Ref: tlmgr _status_202436
+Ref: tlmgr 'd'202514
+Ref: tlmgr 'f'202574
+Ref: tlmgr 'u'202753
+Ref: tlmgr 'r'202799
+Ref: tlmgr 'a'202922
+Ref: tlmgr 'i'203100
+Ref: tlmgr 'I'203219
+Ref: tlmgr _localrev_203321
+Ref: tlmgr _serverrev_203428
+Ref: tlmgr _size_203540
+Ref: tlmgr _runtime_203709
+Ref: tlmgr _esttot_203779
+Node: tlmgr Machine-readable 'option' output203812
+Node: tlmgr ENVIRONMENT VARIABLES204330
+Ref: tlmgr 'TEXLIVE_COMPRESSOR'204841
+Ref: tlmgr 'TEXLIVE_DOWNLOADER'205689
+Ref: tlmgr 'TL_DOWNLOAD_PROGRAM'205712
+Ref: tlmgr 'TL_DOWNLOAD_ARGS'205732
+Ref: tlmgr 'TEXLIVE_PREFER_OWN'206758
+Node: tlmgr AUTHORS AND COPYRIGHT207582
+Node: Index207984
 
 End Tag Table
-
-
-Local Variables:
-coding: utf-8
-End:
diff --git a/source/doc/tlbuild.texi b/source/doc/tlbuild.texi
index 3cf1f3e029683e22fc6f04d26625fcbb0bd560da..d5cdcb22502021b9707de37970848ba3b694e8c6 100644
--- a/source/doc/tlbuild.texi
+++ b/source/doc/tlbuild.texi
@@ -2,7 +2,7 @@
 @setfilename tlbuild.info
 
 @set version 2022
-@set month-year March 2022
+@set month-year May 2022
 
 @set mytitle Building @TeX{} Live (@value{version})
 @settitle @value{mytitle}
@@ -466,7 +466,7 @@ make >&outm || echo fail
 cd texk/dvipdfm-x
 make check
 
-# Run the new binary in the buil tree, finding support files
+# Run the new binary in the build tree, finding support files
 # in a separate tree for a TeX Live release YYYY
 # (Bourne shell syntax):
 TEXMFROOT=/usr/local/texlive/YYYY \
diff --git a/source/libs/README b/source/libs/README
index 7e448399e7aa175a00296bb0ae4051346443d611..f680af8c5973f70f1b56c17f152ecee57bd9d193 100644
--- a/source/libs/README
+++ b/source/libs/README
@@ -1,4 +1,4 @@
-$Id: README 63128 2022-04-24 23:31:44Z kakuto $
+$Id: README 63761 2022-06-29 20:49:55Z kakuto $
 Public domain.  Originally created by Karl Berry, 2005.
 
 Libraries we compile for TeX Live.
@@ -12,7 +12,7 @@ See also comments in ../texk/README.
 cairo 1.16.0 - checked 20oct18
   http://cairographics.org/releases/
 
-freetype2 2.11.1 - checked 03dec21
+freetype2 2.12.1 - checked 07may22
   http://download.savannah.gnu.org/releases/freetype/
 
 gd 2.3.3 - checked 13sep21
@@ -25,8 +25,8 @@ graphite2 1.3.14 - checked 10apr20
   http://sourceforge.net/projects/silgraphite/files/graphite2/
   (requires C++11)
 
-harfbuzz 4.2.1 - checked 25apr22
-  https://github.com/harfbuzz/harfbuzz/releases/download/4.2.1/
+harfbuzz 4.4.1 - checked 30jun22
+  https://github.com/harfbuzz/harfbuzz/releases/download/4.4.1/
 
 icu 70.1 - checked 16jan22
   https://github.com/unicode-org/icu/releases/
diff --git a/source/libs/harfbuzz/ChangeLog b/source/libs/harfbuzz/ChangeLog
index 296b421ac35d6e22dcfc3a5268601ec4ae13f42b..6e5f3ce6239759eebf99258fcbd7ff93976559b8 100644
--- a/source/libs/harfbuzz/ChangeLog
+++ b/source/libs/harfbuzz/ChangeLog
@@ -1,3 +1,22 @@
+2022-07/18  Andreas Scherer  <andreas_github@freenet.de>
+
+	Add a patch 0001-Fix-harfbuzz-4.4.1-for-older-g for older gcc.
+
+2022-06-30  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Import harfbuzz-4.4.1.
+	* version.ac, Makefile.am: Adjusted.
+
+2022-06-28  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Import harfbuzz-4.4.0.
+	* version.ac, Makefile.am: Adjusted.
+
+2022-05-21  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Import harfbuzz-4.3.0.
+	* version.ac: Adjusted.
+
 2022-04-25  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
 
 	Import harfbuzz-4.2.1.
diff --git a/source/libs/harfbuzz/Makefile.am b/source/libs/harfbuzz/Makefile.am
index 7d2413ed846442a353b8b591ecc0e708e838e130..8f2f1a0b87d1b31d241234ff562ba671c06a4597 100644
--- a/source/libs/harfbuzz/Makefile.am
+++ b/source/libs/harfbuzz/Makefile.am
@@ -1,4 +1,4 @@
-## $Id: Makefile.am 62894 2022-04-03 22:28:02Z kakuto $
+## $Id: Makefile.am 63761 2022-06-29 20:49:55Z kakuto $
 ## Proxy Makefile.am to build harfbuzz for TeX Live.
 ##
 ##   Copyright 2016-2017 Karl Berry <tex-live@tug.org>
@@ -47,6 +47,7 @@ libharfbuzz_a_SOURCES =  \
 	@HARFBUZZ_TREE@/src/hb-buffer-verify.cc \
 	@HARFBUZZ_TREE@/src/hb-buffer.cc \
 	@HARFBUZZ_TREE@/src/hb-cache.hh \
+	@HARFBUZZ_TREE@/src/hb-cplusplus.hh \
 	@HARFBUZZ_TREE@/src/hb-cff-interp-common.hh \
 	@HARFBUZZ_TREE@/src/hb-cff-interp-cs-common.hh \
 	@HARFBUZZ_TREE@/src/hb-cff-interp-dict-common.hh \
@@ -188,30 +189,33 @@ libharfbuzz_a_SOURCES += \
 	@HARFBUZZ_TREE@/src/hb-ot-metrics.cc \
 	@HARFBUZZ_TREE@/src/hb-ot-metrics.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-shape.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-fallback.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-joining-list.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-table.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-win1256.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-default.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hangul.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hebrew.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic-table.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-syllabic.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-syllabic.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-thai.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use-table.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-vowel-constraints.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-vowel-constraints.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-fallback.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-joining-list.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-pua.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-table.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-win1256.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-default.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-hangul.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-hebrew.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic-table.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-khmer-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-khmer.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-myanmar-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-myanmar.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-syllabic.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-syllabic.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-thai.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-use-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-use-table.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-use.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-vowel-constraints.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-vowel-constraints.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-normalize.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-normalize.cc \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-fallback.hh \
@@ -223,13 +227,18 @@ libharfbuzz_a_SOURCES += \
 	@HARFBUZZ_TREE@/src/hb-ot-var-gvar-table.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-var-hvar-table.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-var-mvar-table.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-vorg-table.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic-machine.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer-machine.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar-machine.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use-machine.hh
+	@HARFBUZZ_TREE@/src/hb-ot-vorg-table.hh
 ##
 libharfbuzz_a_SOURCES += \
+	@HARFBUZZ_TREE@/src/OT/glyf/glyf.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/glyf-helpers.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/loca.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/path-builder.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/Glyph.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/GlyphHeader.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/SimpleGlyph.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/CompositeGlyph.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/SubsetGlyph.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/Common.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/Sequence.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/SingleSubstFormat1.hh \
@@ -251,7 +260,39 @@ libharfbuzz_a_SOURCES += \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/ExtensionSubst.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/SubstLookupSubTable.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/SubstLookup.hh \
-	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/GSUB.hh
+	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/GSUB.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/CursivePosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkLigPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PairPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/Anchor.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkLigPosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PairPosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ExtensionPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ChainContextPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/Common.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ValueFormat.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorMatrix.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkBasePosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorFormat3.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PosLookup.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkMarkPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PairPosFormat2.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkBasePos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/SinglePos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkArray.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/CursivePos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PosLookupSubTable.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkRecord.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorFormat2.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ContextPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/SinglePosFormat2.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/SinglePosFormat1.hh \
+	@HARFBUZZ_TREE@/src/graph/graph.hh \
+	@HARFBUZZ_TREE@/src/graph/serialize.hh
+
 
 ## Graphite library
 AM_CPPFLAGS += $(GRAPHITE2_INCLUDES)
diff --git a/source/libs/harfbuzz/Makefile.in b/source/libs/harfbuzz/Makefile.in
index 485010636aeb39471da5e4efbd20bad933276081..b65437a5d0cf10865d4ed4ba4d6aef5cab98fb1e 100644
--- a/source/libs/harfbuzz/Makefile.in
+++ b/source/libs/harfbuzz/Makefile.in
@@ -157,18 +157,18 @@ am_libharfbuzz_a_OBJECTS = @HARFBUZZ_TREE@/src/hb-blob.$(OBJEXT) \
 	@HARFBUZZ_TREE@/src/hb-ot-meta.$(OBJEXT) \
 	@HARFBUZZ_TREE@/src/hb-ot-metrics.$(OBJEXT) \
 	@HARFBUZZ_TREE@/src/hb-ot-shape.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-default.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hangul.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hebrew.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic-table.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-syllabic.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-thai.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use.$(OBJEXT) \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-vowel-constraints.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-default.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-hangul.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-hebrew.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic-table.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-khmer.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-myanmar.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-syllabic.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-thai.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-use.$(OBJEXT) \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-vowel-constraints.$(OBJEXT) \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-normalize.$(OBJEXT) \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-fallback.$(OBJEXT) \
 	@HARFBUZZ_TREE@/src/hb-ot-var.$(OBJEXT) \
@@ -222,21 +222,21 @@ am__depfiles_remade = ./$(DEPDIR)/hbtest-dummy.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-meta.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-metrics.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-name.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-arabic.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-default.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hangul.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hebrew.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic-table.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-khmer.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-myanmar.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-syllabic.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-thai.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-use.Po \
-	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-vowel-constraints.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-fallback.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-normalize.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-arabic.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-default.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hangul.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hebrew.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic-table.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-khmer.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-myanmar.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-syllabic.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-thai.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-use.Po \
+	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-vowel-constraints.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-tag.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-var.Po \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-set.Po \
@@ -730,6 +730,7 @@ libharfbuzz_a_SOURCES = @HARFBUZZ_TREE@/src/hb-algs.hh \
 	@HARFBUZZ_TREE@/src/hb-buffer-verify.cc \
 	@HARFBUZZ_TREE@/src/hb-buffer.cc \
 	@HARFBUZZ_TREE@/src/hb-cache.hh \
+	@HARFBUZZ_TREE@/src/hb-cplusplus.hh \
 	@HARFBUZZ_TREE@/src/hb-cff-interp-common.hh \
 	@HARFBUZZ_TREE@/src/hb-cff-interp-cs-common.hh \
 	@HARFBUZZ_TREE@/src/hb-cff-interp-dict-common.hh \
@@ -859,30 +860,33 @@ libharfbuzz_a_SOURCES = @HARFBUZZ_TREE@/src/hb-algs.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-metrics.cc \
 	@HARFBUZZ_TREE@/src/hb-ot-metrics.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-shape.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-fallback.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-joining-list.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-table.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic-win1256.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-default.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hangul.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hebrew.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic-table.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-syllabic.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-syllabic.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-thai.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use-table.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-vowel-constraints.cc \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-vowel-constraints.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-fallback.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-joining-list.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-pua.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-table.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic-win1256.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-default.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-hangul.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-hebrew.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic-table.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-indic.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-khmer-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-khmer.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-myanmar-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-myanmar.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-syllabic.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-syllabic.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-thai.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-use-machine.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-use-table.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-use.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-vowel-constraints.cc \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper-vowel-constraints.hh \
+	@HARFBUZZ_TREE@/src/hb-ot-shaper.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-normalize.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-normalize.cc \
 	@HARFBUZZ_TREE@/src/hb-ot-shape-fallback.hh \
@@ -895,10 +899,15 @@ libharfbuzz_a_SOURCES = @HARFBUZZ_TREE@/src/hb-algs.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-var-hvar-table.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-var-mvar-table.hh \
 	@HARFBUZZ_TREE@/src/hb-ot-vorg-table.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic-machine.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer-machine.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar-machine.hh \
-	@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use-machine.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/glyf.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/glyf-helpers.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/loca.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/path-builder.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/Glyph.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/GlyphHeader.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/SimpleGlyph.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/CompositeGlyph.hh \
+	@HARFBUZZ_TREE@/src/OT/glyf/SubsetGlyph.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/Common.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/Sequence.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/SingleSubstFormat1.hh \
@@ -921,6 +930,37 @@ libharfbuzz_a_SOURCES = @HARFBUZZ_TREE@/src/hb-algs.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/SubstLookupSubTable.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/SubstLookup.hh \
 	@HARFBUZZ_TREE@/src/OT/Layout/GSUB/GSUB.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/CursivePosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkLigPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PairPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/Anchor.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkLigPosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PairPosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ExtensionPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ChainContextPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/Common.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ValueFormat.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorMatrix.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkBasePosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorFormat3.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PosLookup.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkMarkPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PairPosFormat2.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkBasePos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/SinglePos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkArray.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/CursivePos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/PosLookupSubTable.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/MarkRecord.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/AnchorFormat2.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/ContextPos.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/SinglePosFormat2.hh \
+	@HARFBUZZ_TREE@/src/OT/Layout/GPOS/SinglePosFormat1.hh \
+	@HARFBUZZ_TREE@/src/graph/graph.hh \
+	@HARFBUZZ_TREE@/src/graph/serialize.hh \
 	@HARFBUZZ_TREE@/src/hb-graphite2.cc
 @build_TRUE@dist_check_SCRIPTS = harfbuzz.test
 @build_TRUE@TESTS = harfbuzz.test
@@ -1121,40 +1161,40 @@ clean-noinstLIBRARIES:
 @HARFBUZZ_TREE@/src/hb-ot-shape.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-arabic.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-arabic.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-default.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-default.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hangul.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-hangul.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-hebrew.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-hebrew.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-indic-table.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-indic-table.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-indic.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-khmer.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-khmer.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-myanmar.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-myanmar.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-syllabic.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-syllabic.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-thai.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-thai.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-use.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-use.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
-@HARFBUZZ_TREE@/src/hb-ot-shape-complex-vowel-constraints.$(OBJEXT):  \
+@HARFBUZZ_TREE@/src/hb-ot-shaper-vowel-constraints.$(OBJEXT):  \
 	@HARFBUZZ_TREE@/src/$(am__dirstamp) \
 	@HARFBUZZ_TREE@/src/$(DEPDIR)/$(am__dirstamp)
 @HARFBUZZ_TREE@/src/hb-ot-shape-normalize.$(OBJEXT):  \
@@ -1213,21 +1253,21 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-meta.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-metrics.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-name.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-arabic.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-default.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hangul.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hebrew.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic-table.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-khmer.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-myanmar.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-syllabic.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-thai.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-use.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-vowel-constraints.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-fallback.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-normalize.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-arabic.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-default.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hangul.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hebrew.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic-table.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-khmer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-myanmar.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-syllabic.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-thai.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-use.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-vowel-constraints.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-tag.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-var.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@@HARFBUZZ_TREE@/src/$(DEPDIR)/hb-set.Po@am__quote@ # am--include-marker
@@ -1865,21 +1905,21 @@ distclean: distclean-recursive
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-meta.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-metrics.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-name.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-arabic.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-default.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hangul.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hebrew.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic-table.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-khmer.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-myanmar.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-syllabic.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-thai.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-use.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-vowel-constraints.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-fallback.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-normalize.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-arabic.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-default.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hangul.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hebrew.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic-table.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-khmer.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-myanmar.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-syllabic.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-thai.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-use.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-vowel-constraints.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-tag.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-var.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-set.Po
@@ -1968,21 +2008,21 @@ maintainer-clean: maintainer-clean-recursive
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-meta.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-metrics.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-name.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-arabic.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-default.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hangul.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-hebrew.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic-table.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-indic.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-khmer.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-myanmar.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-syllabic.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-thai.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-use.Po
-	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-complex-vowel-constraints.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-fallback.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape-normalize.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shape.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-arabic.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-default.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hangul.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-hebrew.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic-table.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-indic.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-khmer.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-myanmar.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-syllabic.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-thai.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-use.Po
+	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-shaper-vowel-constraints.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-tag.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-ot-var.Po
 	-rm -f @HARFBUZZ_TREE@/src/$(DEPDIR)/hb-set.Po
diff --git a/source/libs/harfbuzz/TLpatches/ChangeLog b/source/libs/harfbuzz/TLpatches/ChangeLog
index a8d0e0d8e22341d984477f5ae671279b146bf78d..0f4a2c04e81f35ca4068d97beb90022cf6eeded1 100644
--- a/source/libs/harfbuzz/TLpatches/ChangeLog
+++ b/source/libs/harfbuzz/TLpatches/ChangeLog
@@ -1,3 +1,24 @@
+2022-07-18  Andreas Scherer  <andreas_github@freenet.de>
+
+	* 0001-Fix-harfbuzz-4.4.1-for-older-g.patch,
+	* 0002-Fix-dispatch_recurse_func-for-older-g.patch:
+	Try to fix namespace/template conundrum for g++ 5 and 6.
+
+2022-06-30  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Imported harfbuzz-4.4.1 source tree from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/4.4.1/
+
+2022-06-28  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Imported harfbuzz-4.4.0 source tree from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/4.4.0/
+
+2022-05-21  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Imported harfbuzz-4.3.0 source tree from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/4.3.0/
+
 2022-04-25  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
 
 	Imported harfbuzz-4.2.1 source tree from:
diff --git a/source/libs/harfbuzz/TLpatches/TL-Changes b/source/libs/harfbuzz/TLpatches/TL-Changes
index aa3c120185af870f4c6cc0599e9a6ce982bf6e03..2d284498e9c4b89ba94cc0313c6b92799971a2ca 100644
--- a/source/libs/harfbuzz/TLpatches/TL-Changes
+++ b/source/libs/harfbuzz/TLpatches/TL-Changes
@@ -1,5 +1,5 @@
-Changes applied to the harfbuzz-4.2.1/ tree as obtained from:
-	https://github.com/harfbuzz/harfbuzz/releases/download/4.2.1/
+Changes applied to the harfbuzz-4.4.1/ tree as obtained from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/4.4.1/
 
 Removed:
 	COPYING
diff --git a/source/libs/harfbuzz/configure b/source/libs/harfbuzz/configure
index d20e622349a8a1d936e9a157b8818135a58a25f8..a5b48895c605ef7b01a19d1f576e007d2675da4e 100755
--- a/source/libs/harfbuzz/configure
+++ b/source/libs/harfbuzz/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for harfbuzz (TeX Live) 4.2.1.
+# Generated by GNU Autoconf 2.71 for harfbuzz (TeX Live) 4.4.1.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -611,8 +611,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='harfbuzz (TeX Live)'
 PACKAGE_TARNAME='harfbuzz--tex-live-'
-PACKAGE_VERSION='4.2.1'
-PACKAGE_STRING='harfbuzz (TeX Live) 4.2.1'
+PACKAGE_VERSION='4.4.1'
+PACKAGE_STRING='harfbuzz (TeX Live) 4.4.1'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1346,7 +1346,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures harfbuzz (TeX Live) 4.2.1 to adapt to many kinds of systems.
+\`configure' configures harfbuzz (TeX Live) 4.4.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1418,7 +1418,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of harfbuzz (TeX Live) 4.2.1:";;
+     short | recursive ) echo "Configuration of harfbuzz (TeX Live) 4.4.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1523,7 +1523,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-harfbuzz (TeX Live) configure 4.2.1
+harfbuzz (TeX Live) configure 4.4.1
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2064,7 +2064,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by harfbuzz (TeX Live) $as_me 4.2.1, which was
+It was created by harfbuzz (TeX Live) $as_me 4.4.1, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -4823,7 +4823,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='harfbuzz--tex-live-'
- VERSION='4.2.1'
+ VERSION='4.4.1'
 
 
 # Some tools Automake needs.
@@ -5034,9 +5034,9 @@ WARNING_CFLAGS=$kpse_cv_warning_cflags
 
 
 HB_VERSION_MAJOR=4
-HB_VERSION_MINOR=2
+HB_VERSION_MINOR=4
 HB_VERSION_MICRO=1
-HB_VERSION=4.2.1
+HB_VERSION=4.4.1
 
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
@@ -8817,7 +8817,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by harfbuzz (TeX Live) $as_me 4.2.1, which was
+This file was extended by harfbuzz (TeX Live) $as_me 4.4.1, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -8885,7 +8885,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-harfbuzz (TeX Live) config.status 4.2.1
+harfbuzz (TeX Live) config.status 4.4.1
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/BUILD.md b/source/libs/harfbuzz/harfbuzz-src/BUILD.md
index 47681ec865da3466b534e6582c6fbf7b979df116..20a52cf4f0c0f0f71f2c9bde6c10c68c05c339ce 100644
--- a/source/libs/harfbuzz/harfbuzz-src/BUILD.md
+++ b/source/libs/harfbuzz/harfbuzz-src/BUILD.md
@@ -5,7 +5,7 @@ Cairo, and GLib. For example, on Ubuntu / Debian, you would do:
 
 whereas on Fedora, RHEL, CentOS, and other Red Hat based systems you would do:
 
-    sudo dnf install meson pkgconfig gtk-doc gcc gcc-c++ freetype-devel glib2-devel cairo-dev
+    sudo dnf install meson pkgconfig gtk-doc gcc gcc-c++ freetype-devel glib2-devel cairo-devel
 
 and on ArchLinux and Manjaro:
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/CONFIG.md b/source/libs/harfbuzz/harfbuzz-src/CONFIG.md
index 15b4ffa3cb20fbc1ba83f5e91112a2b6fb00e80f..0faa359e6bb47c1969bc0474888c4e12c4e1a2e8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/CONFIG.md
+++ b/source/libs/harfbuzz/harfbuzz-src/CONFIG.md
@@ -100,13 +100,14 @@ This is very rarely what you need.  Make sure you understand exactly what you
 are doing.
 
 Defining `HB_NO_FALLBACK_SHAPE` however is pretty harmless.  That removes the
-(unused) "fallback" shaper.
+(unused) "fallback" shaper.  This is defined by the `HB_TINY` profile already
+(more below).
 
 
 ## Thread-safety
 
 By default HarfBuzz builds as a thread-safe library.  The exception is that
-the `HB_TINY` predefined configuring (more below) disables thread-safety.
+the `HB_TINY` predefined configuration (more below) disables thread-safety.
 
 If you do *not* need thread-safety in the library (eg. you always call into
 HarfBuzz from the same thread), you can disable thread-safety by defining
@@ -140,7 +141,7 @@ configurations from the command-line.
 However, configuration can still be overridden from a file.  To do that, add your
 override instructions (mostly `undef` instructions) to a header file and define
 the macro `HB_CONFIG_OVERRIDE_H` to the string containing to that header file's
-name.  HarfBuzz will then include that file at appropriate right place during
+name.  HarfBuzz will then include that file at the appropriate place during
 configuration.
 
 Up until HarfBuzz 3.1.2 the the configuration override header file's name was
@@ -154,4 +155,4 @@ Note that the config option `HB_NO_CFF`, which is enabled by `HB_LEAN` and
 `HB_TINY` does *not* mean that the resulting library won't work with CFF fonts.
 The library can shape valid CFF fonts just fine, with or without this option.
 This option disables (among other things) the code to calculate glyph extents
-for CFF fonts.
+for CFF fonts, which many clients might not need.
diff --git a/source/libs/harfbuzz/harfbuzz-src/ChangeLog b/source/libs/harfbuzz/harfbuzz-src/ChangeLog
index db86a79026950408f02d88194d357c1bdf03cb09..59b5b780d27a69f75bb9babaefcb8506b4ba3943 100644
--- a/source/libs/harfbuzz/harfbuzz-src/ChangeLog
+++ b/source/libs/harfbuzz/harfbuzz-src/ChangeLog
@@ -1,3 +1,8031 @@
+commit 096aaa62a6e0d07c02a4894fc036efc927e5aaf9
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Wed Jun 29 07:30:05 2022 +0200
+
+    4.4.1
+
+ NEWS             | 7 +++++++
+ configure.ac     | 2 +-
+ meson.build      | 2 +-
+ src/hb-version.h | 4 ++--
+ 4 files changed, 11 insertions(+), 4 deletions(-)
+
+commit 4d1d7aec8d7430ea062241b46be5fa78660df2f4
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Wed Jun 29 07:29:39 2022 +0200
+
+    [docs] Add missing 4.4.0 index
+
+ docs/harfbuzz-docs.xml | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 050f169078e272abb56c35fe3ec00a1c6238e518
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 18:52:27 2022 -0600
+
+    [GPOS/kerx] Call into impl namespace from kerx
+
+ src/hb-aat-layout-kerx-table.hh | 8 ++++----
+ src/hb-ot-layout-gpos-table.hh  | 3 ---
+ 2 files changed, 4 insertions(+), 7 deletions(-)
+
+commit 910a137f4ef61b4986fb9071253e25b4f9c56c06
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed Jun 29 00:05:35 2022 +0000
+
+    [reorg] Fix propagate_attachment_offsets definition.
+
+ src/OT/Layout/GPOS.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 3fbf2dece7f2a0b5820e01846da76114babe5a2f
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue Jun 28 23:55:32 2022 +0000
+
+    [reorg] Move OT::Layout::GPOS_impl::GPOS to OT::Layout::GPOS.
+
+ src/Makefile.sources             | 29 +++++++++++++++++++++++++++
+ src/OT/Layout/{GPOS => }/GPOS.hh | 42 ++++++++++++++++++----------------------
+ src/OT/Layout/GPOS/PosLookup.hh  |  2 +-
+ src/hb-ot-layout-gpos-table.hh   |  2 +-
+ src/hb-ot-layout.cc              |  2 +-
+ src/hb-subset-plan.cc            |  2 +-
+ src/hb-subset.cc                 |  2 +-
+ src/meson.build                  | 29 +++++++++++++++++++++++++++
+ 8 files changed, 82 insertions(+), 28 deletions(-)
+
+commit 88ef3c5a9abc22927365e9c60d7aa0e3e8a339b9
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue Jun 28 23:26:49 2022 +0000
+
+    [reorg] Change OT::Layout::GPOS to OT::Layout::GPOS_impl.
+
+ src/OT/Layout/GPOS/Anchor.hh             |  2 +-
+ src/OT/Layout/GPOS/AnchorFormat1.hh      |  2 +-
+ src/OT/Layout/GPOS/AnchorFormat2.hh      |  2 +-
+ src/OT/Layout/GPOS/AnchorFormat3.hh      |  2 +-
+ src/OT/Layout/GPOS/AnchorMatrix.hh       |  2 +-
+ src/OT/Layout/GPOS/ChainContextPos.hh    |  2 +-
+ src/OT/Layout/GPOS/Common.hh             |  2 +-
+ src/OT/Layout/GPOS/ContextPos.hh         |  2 +-
+ src/OT/Layout/GPOS/CursivePos.hh         |  2 +-
+ src/OT/Layout/GPOS/CursivePosFormat1.hh  |  4 ++--
+ src/OT/Layout/GPOS/ExtensionPos.hh       |  2 +-
+ src/OT/Layout/GPOS/GPOS.hh               |  6 +++---
+ src/OT/Layout/GPOS/MarkArray.hh          |  2 +-
+ src/OT/Layout/GPOS/MarkBasePos.hh        |  2 +-
+ src/OT/Layout/GPOS/MarkBasePosFormat1.hh |  2 +-
+ src/OT/Layout/GPOS/MarkLigPos.hh         |  2 +-
+ src/OT/Layout/GPOS/MarkLigPosFormat1.hh  |  2 +-
+ src/OT/Layout/GPOS/MarkMarkPos.hh        |  2 +-
+ src/OT/Layout/GPOS/MarkMarkPosFormat1.hh |  2 +-
+ src/OT/Layout/GPOS/MarkRecord.hh         |  2 +-
+ src/OT/Layout/GPOS/PairPos.hh            |  2 +-
+ src/OT/Layout/GPOS/PairPosFormat1.hh     |  2 +-
+ src/OT/Layout/GPOS/PairPosFormat2.hh     |  2 +-
+ src/OT/Layout/GPOS/PosLookup.hh          |  2 +-
+ src/OT/Layout/GPOS/PosLookupSubTable.hh  |  2 +-
+ src/OT/Layout/GPOS/SinglePos.hh          |  2 +-
+ src/OT/Layout/GPOS/SinglePosFormat1.hh   |  2 +-
+ src/OT/Layout/GPOS/SinglePosFormat2.hh   |  2 +-
+ src/OT/Layout/GPOS/ValueFormat.hh        |  2 +-
+ src/hb-ot-layout-gpos-table.hh           | 20 ++++++++++----------
+ src/hb-ot-layout.cc                      |  2 +-
+ src/hb-subset-plan.cc                    |  2 +-
+ src/hb-subset.cc                         |  2 +-
+ 33 files changed, 45 insertions(+), 45 deletions(-)
+
+commit 49ddf069e02bea6786f47780cbd2e5917e9364a5
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue Jun 28 23:15:07 2022 +0000
+
+    [reorg] Move GPOS reverse_cursive_minor_offset implementation into new directory layout.
+
+ src/OT/Layout/GPOS/CursivePosFormat1.hh | 25 ++++++++++++++-
+ src/hb-ot-layout-gpos-table.hh          | 56 ++++++---------------------------
+ 2 files changed, 33 insertions(+), 48 deletions(-)
+
+commit 74f45f7c2ac4bb1d465926ebf2c3fba5a4572767
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Jun 24 23:14:30 2022 +0000
+
+    [reorg] Move remaining GPOS lookup types to new directory.
+
+ src/OT/Layout/GPOS/ChainContextPos.hh    |  14 ++
+ src/OT/Layout/GPOS/ContextPos.hh         |  14 ++
+ src/OT/Layout/GPOS/ExtensionPos.hh       |  17 ++
+ src/OT/Layout/GPOS/GPOS.hh               | 101 +++++++-
+ src/OT/Layout/GPOS/MarkBasePos.hh        |   1 +
+ src/OT/Layout/GPOS/MarkLigPos.hh         |   1 +
+ src/OT/Layout/GPOS/MarkMarkPos.hh        |  36 +++
+ src/OT/Layout/GPOS/MarkMarkPosFormat1.hh | 227 ++++++++++++++++++
+ src/OT/Layout/GPOS/PosLookup.hh          |   1 -
+ src/OT/Layout/GPOS/PosLookupSubTable.hh  |   6 +-
+ src/OT/Layout/GPOS/ValueFormat.hh        |   2 +
+ src/hb-ot-layout-gpos-table.hh           | 382 +------------------------------
+ src/hb-ot-layout.cc                      |  15 +-
+ src/hb-subset-plan.cc                    |   6 +-
+ src/hb-subset.cc                         |   3 +-
+ 15 files changed, 442 insertions(+), 384 deletions(-)
+
+commit 197d9a5c994eb41c8c89b7b958b26b1eacfeeb00
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Jun 24 22:36:14 2022 +0000
+
+    [reorg] Move more GPOS lookups to new directory.
+
+ src/OT/Layout/GPOS/Anchor.hh             |  84 +++
+ src/OT/Layout/GPOS/AnchorFormat1.hh      |  46 ++
+ src/OT/Layout/GPOS/AnchorFormat2.hh      |  58 ++
+ src/OT/Layout/GPOS/AnchorFormat3.hh      |  70 +++
+ src/OT/Layout/GPOS/AnchorMatrix.hh       |  77 +++
+ src/OT/Layout/GPOS/Common.hh             |  14 +
+ src/OT/Layout/GPOS/CursivePosFormat1.hh  |   2 +
+ src/OT/Layout/GPOS/MarkArray.hh          | 113 ++++
+ src/OT/Layout/GPOS/MarkBasePos.hh        |  34 ++
+ src/OT/Layout/GPOS/MarkBasePosFormat1.hh | 217 ++++++++
+ src/OT/Layout/GPOS/MarkLigPos.hh         |  34 ++
+ src/OT/Layout/GPOS/MarkLigPosFormat1.hh  | 244 +++++++++
+ src/OT/Layout/GPOS/MarkRecord.hh         |  52 ++
+ src/OT/Layout/GPOS/PosLookupSubTable.hh  |   2 +
+ src/hb-ot-layout-gpos-table.hh           | 875 +------------------------------
+ 15 files changed, 1049 insertions(+), 873 deletions(-)
+
+commit c7307ca06ab3126f7783093a27388745af1d646b
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Jun 24 22:01:02 2022 +0000
+
+    [reorg] Begin moving GPOS into the new directory layout.
+
+ src/OT/Layout/GPOS/Common.hh            |   18 +
+ src/OT/Layout/GPOS/CursivePos.hh        |   35 +
+ src/OT/Layout/GPOS/CursivePosFormat1.hh |  256 ++++
+ src/OT/Layout/GPOS/GPOS.hh              |   69 ++
+ src/OT/Layout/GPOS/PairPos.hh           |   38 +
+ src/OT/Layout/GPOS/PairPosFormat1.hh    |  420 +++++++
+ src/OT/Layout/GPOS/PairPosFormat2.hh    |  314 +++++
+ src/OT/Layout/GPOS/PosLookup.hh         |   80 ++
+ src/OT/Layout/GPOS/PosLookupSubTable.hh |   73 ++
+ src/OT/Layout/GPOS/SinglePos.hh         |   98 ++
+ src/OT/Layout/GPOS/SinglePosFormat1.hh  |  124 ++
+ src/OT/Layout/GPOS/SinglePosFormat2.hh  |  140 +++
+ src/OT/Layout/GPOS/ValueFormat.hh       |  327 +++++
+ src/hb-ot-layout-gpos-table.hh          | 1975 ++-----------------------------
+ 14 files changed, 2075 insertions(+), 1892 deletions(-)
+
+commit 7b0d8d9d18fe4d0371b62bdc87b9d6e52e05f6e2
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Wed Jun 29 00:34:09 2022 +0200
+
+    [meson] Remove ttf-parser wrap
+    
+    We don’t have a ttf-parser dependency anymore.
+
+ Makefile.am                 | 1 -
+ subprojects/ttf-parser.wrap | 5 -----
+ 2 files changed, 6 deletions(-)
+
+commit 9909d11f6f7b3eeddc00a981e24f26559d9ef3b7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 15:59:40 2022 -0600
+
+    [indic generator] Fix regression
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3690
+
+ src/gen-indic-table.py                                   |   2 +-
+ src/hb-ot-shaper-indic-table.cc                          |  12 ++++++------
+ .../fonts/e716f6bd00a108d186b7e9f47b4515565f784f36.ttf   | Bin 0 -> 6260 bytes
+ test/shape/data/in-house/tests/indic-special-cases.tests |   1 +
+ 4 files changed, 8 insertions(+), 7 deletions(-)
+
+commit 4499ae0225172ab7590619219b21fe0a0c14d66e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 15:43:57 2022 -0600
+
+    [coretext] Fix positioning of out-of-order glyphs
+    
+    Unfortunately this now generates negative advances. To be fixed...
+
+ src/hb-coretext.cc | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+commit 58d2e9309952c139a4fa05ed44c22bb712fd6cd4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 15:38:58 2022 -0600
+
+    [coretext] Fix up clusters only if needed
+
+ src/hb-coretext.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit eaba5e74a9285647739dfc563471321d4d0ec9e0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 13:47:49 2022 -0600
+
+    [directwrite] Simplify
+
+ src/hb-directwrite.cc | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+commit 33e3bf2d79c9b5598ba373ab093ec8b71404794b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 13:46:04 2022 -0600
+
+    [font] Drop caches on variation changes
+
+ src/hb-font.cc | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+commit c90130e6252b2914d9fdf9007a62fc924ef3d963
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 13:30:44 2022 -0600
+
+    [coretext] Remove old hack now that font layer takes care...
+    
+    of invalidating font data when font settings change.
+
+ src/hb-coretext.cc | 37 +++----------------------------------
+ 1 file changed, 3 insertions(+), 34 deletions(-)
+
+commit c1c78ade71fabe826f695704acda836c7bc21bf4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 28 13:19:12 2022 -0600
+
+    [font] When font changes, drop font shaper data
+    
+    https://github.com/harfbuzz/harfbuzz/issues/3683#issuecomment-1168016509
+
+ src/hb-font.hh      | 2 ++
+ src/hb-machinery.hh | 2 +-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+commit 34c6c0193c57110219eecd58cc67daffcd84d071
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 20:26:19 2022 -0600
+
+    [glyf] Fix byterange check again
+
+ src/OT/glyf/SimpleGlyph.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 20572f914ce7c386e9ffb9cc2833b1f43d0025b2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 20:22:09 2022 -0600
+
+    [glyf] Move read_flags into a function
+
+ src/OT/glyf/SimpleGlyph.hh | 41 ++++++++++++++++++++++++-----------------
+ 1 file changed, 24 insertions(+), 17 deletions(-)
+
+commit 7b0fc0be539ae02af71a7fa076a96c779c116b68
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Mon Jun 27 21:18:36 2022 -0400
+
+    [test] Test the reordering of U+0E33 and U+0EB3
+
+ test/shape/README.md                               |   2 +-
+ test/shape/data/in-house/Makefile.sources          |   1 +
+ .../63a539a90a371ccf028dc2dcced9b63b07163be7.ttf   | Bin 0 -> 1656 bytes
+ test/shape/data/in-house/meson.build               |   1 +
+ test/shape/data/in-house/tests/sara-am.tests       |  52 +++++++++++++++++++++
+ 5 files changed, 55 insertions(+), 1 deletion(-)
+
+commit 3c34b9ec30f4d07414a8053ae39be555001c64cc
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 19:45:58 2022 -0600
+
+    [mingw2] Turn optimization flag on
+
+ mingw-configure.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 31e985d7d1089e50c80c7186ebf1e475cc6c238d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 16:32:02 2022 -0600
+
+    [buffer] Likely that not messaging
+
+ src/hb-buffer.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 4be074e2cb6d94fb9dfb833e713e30c0e6c6cc72
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 16:12:42 2022 -0600
+
+    [gvar] Whitespace
+
+ src/hb-ot-var-gvar-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit a96647841afb02579caf7eb926dfeb7979cbfbe7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:41:02 2022 -0600
+
+    [gvar] Optimize apply_deltas_to_points
+
+ src/hb-ot-var-gvar-table.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit e9af9062c0e1be1f479a43ad878cda622c781a56
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:38:42 2022 -0600
+
+    [gvar] Optimize unpack_deltas
+
+ src/hb-ot-var-gvar-table.hh | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit 573e77280b4f78c9a880c4abfa1f86d70354bc79
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:35:28 2022 -0600
+
+    [gvar] Optimize unpack_deltas
+
+ src/hb-ot-var-gvar-table.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 698f51464c4a8f2e7e49c2e99fb0c8b4bfce9493
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:30:19 2022 -0600
+
+    [gvar] Share vector allocation across delta-sets
+
+ src/hb-ot-var-gvar-table.hh | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit 39e280c256894662d4591b4de7517b7e1147c66f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:25:50 2022 -0600
+
+    [gvar] Handle a couple of error conditions
+
+ src/hb-ot-var-gvar-table.hh | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 5be6e5dd577b8680ee5ffdedeec93638dffb5547
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:22:16 2022 -0600
+
+    [gvar] Rewrite linear interpolation
+
+ src/hb-ot-var-gvar-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 57519b532da6fb1ef2442eb1eda26192f9617552
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:18:56 2022 -0600
+
+    [gvar] Use pointer-to-member instead of function
+
+ src/hb-ot-var-gvar-table.hh | 20 +++++++++-----------
+ 1 file changed, 9 insertions(+), 11 deletions(-)
+
+commit ab15fe082ab79b608d9ff346c92dcc000404343d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:13:10 2022 -0600
+
+    [gvar] Handle a couple of error conditions
+
+ src/hb-ot-var-gvar-table.hh | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+commit b7e9e8785c75b1a9b97a6eb648e5887093a3257a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 15:08:51 2022 -0600
+
+    [gvar] Optimize deltas and points loading
+
+ src/hb-ot-var-gvar-table.hh | 34 ++++++++++++++++------------------
+ 1 file changed, 16 insertions(+), 18 deletions(-)
+
+commit 6e72c2e3faf84634ac98d31be2344c2d604bed14
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 14:02:15 2022 -0600
+
+    [glyf] Add an assertion
+
+ src/OT/glyf/SimpleGlyph.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 5da341ce92352860fb43296cdeb7ed4141ff2864
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 13:29:22 2022 -0600
+
+    [map] Another try at hiding minus1
+    
+    To fix https://github.com/harfbuzz/harfbuzz/issues/3684
+
+ src/hb-map.hh    | 8 +++++++-
+ src/hb-static.cc | 3 +++
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+commit c72d3104ed0fe1fa91eb1ff02b0761578161edb0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 13:31:05 2022 -0600
+
+    [map] Return const reference in operator[]
+
+ src/hb-map.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 69d53f3e7fde164a1fe7bb5f812045e5275893a7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 13:17:10 2022 -0600
+
+    [map] Make default_value() inline
+    
+    See if it make fix https://github.com/harfbuzz/harfbuzz/issues/3684
+
+ src/hb-map.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 3a0e27e794bd7a0a49ed5be41c044d9be910ea07
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 13:07:39 2022 -0600
+
+    [glyf] Move comment
+
+ src/OT/glyf/Glyph.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit d0836dee7a6b81a4d037b3e1dc841416cc14bf87
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 13:05:58 2022 -0600
+
+    [glyf] Minor typo change
+
+ src/OT/glyf/SimpleGlyph.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 5cca25e5d0d976020eead5113da82aae11c0d2ae
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 13:03:06 2022 -0600
+
+    [glyf] Accumulate points as int
+    
+    Everything is int at this stage.
+    Doesn't seem to matter for performance though.
+
+ src/OT/glyf/SimpleGlyph.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit d6f60b3c190e3fd050f8c4a0613939d417f53a69
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 13:00:08 2022 -0600
+
+    [glyf] Minor optimization
+
+ src/OT/glyf/SimpleGlyph.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit b30a3dcba3e69f93e551fb1736785dcec5a0ca70
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:56:21 2022 -0600
+
+    [glyf] Another bounds check
+    
+    Very unlikely that is needed but technically possible.
+
+ src/OT/glyf/SimpleGlyph.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 2e9dbdcbbe17a1c55e39f1d4acef023e5a26842f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:55:13 2022 -0600
+
+    [glyf] Protect against an unlikely overflow
+
+ src/OT/glyf/SimpleGlyph.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 8537d681728e141550b4470b591fa059f6ca2670
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Mon Jun 27 20:51:16 2022 +0200
+
+    4.4.0
+
+ NEWS             | 34 ++++++++++++++++++++++++++++++++++
+ configure.ac     |  2 +-
+ meson.build      |  2 +-
+ src/hb-buffer.h  |  4 ++--
+ src/hb-font.cc   |  4 ++--
+ src/hb-ft.cc     |  2 +-
+ src/hb-map.cc    |  4 ++--
+ src/hb-set.cc    |  2 +-
+ src/hb-version.h |  4 ++--
+ 9 files changed, 46 insertions(+), 12 deletions(-)
+
+commit f1fb8c4489f3530badaab6c4f1172f044febc346
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:41:46 2022 -0600
+
+    [glyf] Optimize Glyph layout
+
+ src/OT/glyf/Glyph.hh | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit 34e3f561b53de6feaa6d3f2dfea6014a12661d86
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:39:35 2022 -0600
+
+    [glyf] Fix a bug I introduced recently
+    
+    Pass gid to Glyph in trim_padding codepath.
+
+ src/OT/glyf/glyf.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 97cbc2d40a2af1ecb3c1a8c765807e56ffdb4dba
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:37:11 2022 -0600
+
+    [gvar] Remove condition that font num_coords should match gvar's
+
+ src/hb-ot-var-gvar-table.hh | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+commit 19cbfb9ce962ff18234f564de4c578209dd0d319
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:32:18 2022 -0600
+
+    [glyf] Relax a condition that font num_coords be equal to gvar's
+    
+    gvar itself still checks the same.
+
+ src/OT/glyf/glyf.hh | 2 +-
+ src/hb-coretext.cc  | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit d5cfbaa0684d4bf33e21bf1609f730436312bc10
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:24:20 2022 -0600
+
+    [glyf] Optimize composite points loading
+
+ src/OT/glyf/Glyph.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 32dc0641e17a339c5d429fc5f59c1b086dc96c47
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:22:06 2022 -0600
+
+    [glyf] Remove an unnecessary condition
+
+ src/OT/glyf/Glyph.hh | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit 95bfa0913da18bc752030ca814c9339bbc76159b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:09:42 2022 -0600
+
+    [gvar] Optimize translate()
+
+ src/hb-ot-var-gvar-table.hh | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit 9f067582b80ade3235f4c290afbb65b2851ada78
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 12:08:47 2022 -0600
+
+    [gvar] Optimize transform()
+
+ src/hb-ot-var-gvar-table.hh | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit cfc57ef862c911c2e2c6dc9992e866373b6ba89f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 11:56:28 2022 -0600
+
+    [glyf] Optimize contour_point_t layout
+
+ src/hb-ot-var-gvar-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 75ca78a6bbbfda8d8fcb363ef71d3012949aee5e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 11:55:37 2022 -0600
+
+    [glyf] Optimize hb_contour_points_t::extend
+
+ src/hb-ot-var-gvar-table.hh | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+commit 98fbe87a26f5b4ef480c1f68526479e0b7121ddd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 11:48:59 2022 -0600
+
+    [benchmark-font] Disable quadratic callback
+    
+    We are interested in the quadratic-to-cubic codepath benchmarking.
+
+ perf/benchmark-font.cc | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 04c4767150fe398d1f687e044cfc014151ad5e26
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Sun Jun 26 21:44:51 2022 -0400
+
+    [lao] Decompose and reorder U+0EB3 around U+0EBB
+
+ src/hb-algs.hh           | 5 +++++
+ src/hb-ot-shaper-thai.cc | 8 ++++----
+ 2 files changed, 9 insertions(+), 4 deletions(-)
+
+commit d3308f4713eb9087a300c0db9b1ca06015180e7f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 11:28:44 2022 -0600
+
+    [font] Optimize font scaling further
+
+ src/hb-font.cc       |  9 ++++-----
+ src/hb-font.hh       | 37 +++++++++++++++++++------------------
+ test/api/test-draw.c |  2 +-
+ 3 files changed, 24 insertions(+), 24 deletions(-)
+
+commit e72506d08545c17c8f71a2d7167828a3c6aef8db
+Author: Matthias Clasen <mclasen@redhat.com>
+Date:   Mon Jun 27 07:41:13 2022 -0400
+
+    Fix the annotation for hb_blob_get_data
+    
+    This function will return NULL for the the
+    empty blob. That is important information for
+    bindings that treat nullability as a type trait.
+
+ src/hb-blob.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 3c49a6a60a26bacfa0fc1284566db7a9c680e9c2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 27 10:57:51 2022 -0600
+
+    [glyf] Fix an allocation error
+    
+    Try fixing assertion failure found by fuzzers:
+    
+    hb-draw-fuzzer: ../../src/harfbuzz/src/OT/glyf/glyf.hh:175: bool OT::glyf_accelerator_t::get_points(hb_font_t *, hb_codepoint_t, T) const [T = OT::glyf_impl::path_builder_t]: Assertion `count >= glyf_impl::PHANTOM_COUNT' failed.
+
+ src/OT/glyf/Glyph.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 556e7078f0cc283d95d6e5814ed3c64a2c02d9a8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 17:48:43 2022 -0600
+
+    [font] Optimize glyph scaling
+
+ src/hb-font.cc       |  4 ++++
+ src/hb-font.hh       | 13 ++++++++-----
+ test/api/test-draw.c |  2 +-
+ 3 files changed, 13 insertions(+), 6 deletions(-)
+
+commit a1c45bbb55a753ac6f6ad166fc87097dac95dea6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 17:30:16 2022 -0600
+
+    [glyf] Minor simplify
+
+ src/OT/glyf/SimpleGlyph.hh | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+commit a21a9bb855de199bacb80a15049340d818edcd41
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 17:24:29 2022 -0600
+
+    [glyf] Optimize flags decoding byte range checking
+
+ src/OT/glyf/SimpleGlyph.hh | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit 0f1fdf461c1abd1fa0674d0619402942494bd69c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 17:22:45 2022 -0600
+
+    [glyf] Optimize points decoding byte range checking
+
+ src/OT/glyf/SimpleGlyph.hh | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit 51cfcf29571dba41281fbb96fb1dd943f2ef1e44
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 17:04:47 2022 -0600
+
+    [glyf] Optimize points decoding
+
+ src/OT/glyf/SimpleGlyph.hh | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+commit 86b702250361485f0d57dcf6fe4a38eaacf0d87e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:56:24 2022 -0600
+
+    [glyf] Optimize flag decoding
+
+ src/OT/glyf/SimpleGlyph.hh | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+commit 30d58bfd0f4b3c0b97740281ae680c3164dd17d0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:47:21 2022 -0600
+
+    [glyf] Don't translate/transform components if has no effect
+
+ src/hb-ot-var-gvar-table.hh | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+commit 2bb0fa878a3c1f51bb9d944829465bc1a4262d37
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:43:05 2022 -0600
+
+    [glyf] Remove unneeded point init()
+
+ src/OT/glyf/Glyph.hh | 3 ---
+ 1 file changed, 3 deletions(-)
+
+commit fc72a1d22dc9efd52a9fa43143673253b20b0c69
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:42:01 2022 -0600
+
+    [glyf] Add a pre-allocation for phantom points
+
+ src/OT/glyf/SimpleGlyph.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 8d242aaa8ba7a2669fd7f1b393a2ba130ffb8544
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:36:08 2022 -0600
+
+    [glyf] Rewrite a loop harmlessly
+    
+    I hope...
+
+ src/OT/glyf/glyf.hh | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+commit cf57f04ddb8a731b13c9e09cb40c43253faa1fdb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:26:17 2022 -0600
+
+    [glyf/path-builder] Optimize scaling code
+    
+    Scale each point once upon entry to function.
+    
+    This makes our shape fetching code as fast as FreeType for all
+    benchmark cases now.
+
+ src/OT/glyf/path-builder.hh | 32 ++++++++++++++++----------------
+ 1 file changed, 16 insertions(+), 16 deletions(-)
+
+commit 36dd5d32fbcce76b5d58496ca8075bc5c71ae2de
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:16:43 2022 -0600
+
+    [draw] Use multiplication instead of division in quadratic conversion
+
+ src/hb-draw.cc | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+commit b095df1343d6d883f602ef4e18230dd6d1d0a816
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:12:50 2022 -0600
+
+    [glyf/path-builder] Use operator bool for style
+
+ src/OT/glyf/path-builder.hh | 17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+commit abb433d0f4736376d82e488b7790c02ec98351d2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:09:32 2022 -0600
+
+    [glyf] Avoid a copy of points in shape fetching for simple glyphs
+    
+    Matches performance with freetype now.
+
+ src/OT/glyf/Glyph.hh | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+commit 7eac779abf14243124af2c6e89cff71e18e41cb3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:02:27 2022 -0600
+
+    Revert "Revert "[glyf] Optimize shape loading""
+    
+    This reverts commit 164bd288cfe66f1742183ab38fa9bd121b1cd8a0.
+
+ src/OT/glyf/Glyph.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 164bd288cfe66f1742183ab38fa9bd121b1cd8a0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 16:01:15 2022 -0600
+
+    Revert "[glyf] Optimize shape loading"
+    
+    This reverts commit f0819301b74871c4c0a58e16918d3f8df2c6f74d.
+    
+    Broke tests. To be debugged and redone.
+
+ src/OT/glyf/Glyph.hh | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit f0819301b74871c4c0a58e16918d3f8df2c6f74d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 15:52:54 2022 -0600
+
+    [glyf] Optimize shape loading
+    
+    Do away with a copy for simple glyph load.
+
+ src/OT/glyf/Glyph.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit ea5131507a723b4858f6a90584351ac14a990ecb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 13:30:11 2022 -0600
+
+    [mingw] Build with directwrite if available
+
+ configure.ac       | 2 +-
+ mingw-configure.sh | 2 ++
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+commit 65b066f18e835d7cba57bea84fc5b244ad5e5b90
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 26 13:18:00 2022 -0600
+
+    [glyf/path-builder] Simplify initialization
+
+ src/OT/glyf/path-builder.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit b2abd5c7e8c757db793243856debfd57eefb320a
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Sat Jun 25 22:55:50 2022 -0400
+
+    [thai] Reword to include all relevant marks
+
+ src/hb-ot-shaper-thai.cc | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 2d4557fe04504d97db02e697482388a91fc9613e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 19:28:33 2022 -0600
+
+    [glyf/SimpleGlyph] Use member pointer instead of lambda
+
+ src/OT/glyf/SimpleGlyph.hh | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 9ce97730404300eed6d8ec1ea806fae8f4aab077
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 19:24:05 2022 -0600
+
+    [glyf/SimpleGlyph] Minor use constructor for contour_point_t
+
+ src/OT/glyf/SimpleGlyph.hh  | 1 -
+ src/hb-ot-var-gvar-table.hh | 7 ++++---
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+commit f897978f21c8bf3f6a8f10004e1c1e5dc8619c6d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 19:17:56 2022 -0600
+
+    [glyf] Adjust a check-range
+
+ src/OT/glyf/SimpleGlyph.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit db039d97ff991deecbdffb034b23f4ce086fa562
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 19:06:27 2022 -0600
+
+    [glyf/Composite] Make glyphIndex public
+
+ src/OT/glyf/CompositeGlyph.hh | 5 +----
+ src/OT/glyf/Glyph.hh          | 2 +-
+ src/OT/glyf/SubsetGlyph.hh    | 4 ++--
+ src/hb-subset-plan.cc         | 2 +-
+ 4 files changed, 5 insertions(+), 8 deletions(-)
+
+commit 1b14bf8aa842d1e13728a2b5675458adcd9b9de1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 19:03:30 2022 -0600
+
+    [glyf] Rename CompositeGlyphChain to CompositeGlyphRecord
+
+ src/OT/glyf/CompositeGlyph.hh | 22 +++++++++++-----------
+ src/OT/glyf/SubsetGlyph.hh    |  2 +-
+ 2 files changed, 12 insertions(+), 12 deletions(-)
+
+commit a5ac7f2ea65849b3926cb27a49bc6e5112510ae8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 19:01:43 2022 -0600
+
+    [glyf/composite_iter_t] Renames
+
+ src/OT/glyf/CompositeGlyph.hh | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+commit d15260ca9580d04d04829eefbcb239112afef2ed
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 19:53:11 2022 -0600
+
+    [gpos] Limit recursion depth in propagate_attachment_offsets()
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/2927
+
+ src/hb-ot-layout-gpos-table.hh | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+commit 449bdeed5f2272ebe1176fa371833941a98b1e8f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 18:31:31 2022 -0600
+
+    [glyf] Rename get_iterator() to iter()
+    
+    That's the standard name.
+
+ src/OT/glyf/CompositeGlyph.hh | 8 ++++----
+ src/OT/glyf/Glyph.hh          | 4 ++--
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+commit 11d267067601ac185858075d01fa68c26c0224b9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 18:19:09 2022 -0600
+
+    [glyf] Split off glyf-helpers.hh
+
+ src/Makefile.sources        |  1 +
+ src/OT/glyf/glyf-helpers.hh | 90 +++++++++++++++++++++++++++++++++++++++++++++
+ src/OT/glyf/glyf.hh         | 77 ++------------------------------------
+ src/meson.build             |  1 +
+ 4 files changed, 96 insertions(+), 73 deletions(-)
+
+commit 7c4b8c9bf413389f536ad32a53b44144b26329c4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 18:14:42 2022 -0600
+
+    [glyf] path-builder minor header guards fix test
+
+ src/OT/glyf/path-builder.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 499c6379225e39b5e96752246b97e6b4e0ddd489
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 18:07:49 2022 -0600
+
+    [glyf] Split off path-builder.hh
+
+ src/Makefile.sources        |   1 +
+ src/OT/glyf/glyf.hh         | 120 +--------------------------------------
+ src/OT/glyf/path-builder.hh | 134 ++++++++++++++++++++++++++++++++++++++++++++
+ src/meson.build             |   1 +
+ 4 files changed, 138 insertions(+), 118 deletions(-)
+
+commit 13aadc89617f1695bfcf152c96307ec97969c21e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 18:03:15 2022 -0600
+
+    [glyf] Split off CompositeGlyph.hh
+
+ src/Makefile.sources          |   1 +
+ src/OT/glyf/CompositeGlyph.hh | 261 ++++++++++++++++++++++++++++++++++++++++++
+ src/OT/glyf/Glyph.hh          | 246 +--------------------------------------
+ src/meson.build               |   1 +
+ 4 files changed, 264 insertions(+), 245 deletions(-)
+
+commit f0ec2b728e0576611b77ecbd7527044ee194191d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:55:16 2022 -0600
+
+    [glyf] Split off SimpleGlyph.hh
+
+ src/Makefile.sources       |   1 +
+ src/OT/glyf/Glyph.hh       | 184 +----------------------------------------
+ src/OT/glyf/SimpleGlyph.hh | 200 +++++++++++++++++++++++++++++++++++++++++++++
+ src/meson.build            |   1 +
+ 4 files changed, 203 insertions(+), 183 deletions(-)
+
+commit 8ed78627f0fd1f6957eadffdfab4c8c61d0bfcbb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:53:13 2022 -0600
+
+    [glyf] Split off GlyphHeader.hh
+
+ src/Makefile.sources       |  1 +
+ src/OT/glyf/Glyph.hh       | 33 ++-----------------------------
+ src/OT/glyf/GlyphHeader.hh | 48 ++++++++++++++++++++++++++++++++++++++++++++++
+ src/meson.build            |  1 +
+ 4 files changed, 52 insertions(+), 31 deletions(-)
+
+commit 81315a3016803d17d95a72c9fcfc6ab2a841e14d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:50:44 2022 -0600
+
+    [glyf] Namespace implementation in glyf_impl
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3677
+
+ src/OT/glyf/Glyph.hh       |  4 ++++
+ src/OT/glyf/SubsetGlyph.hh |  2 ++
+ src/OT/glyf/glyf.hh        | 42 +++++++++++++++++++++---------------------
+ 3 files changed, 27 insertions(+), 21 deletions(-)
+
+commit be1d4bcf29ba87747252eff087a89eda5b4fa007
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:45:21 2022 -0600
+
+    [glyf] Add fast __end__ to composite iterator
+
+ src/OT/glyf/Glyph.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit ef250eea7e1b08bdf1d324bda8abaeb7b31dc8e4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:37:59 2022 -0600
+
+    [glyf] Move a few structs out of Glyph{}
+
+ src/OT/glyf/Glyph.hh | 415 ++++++++++++++++++++++++++-------------------------
+ src/OT/glyf/glyf.hh  |   4 +-
+ 2 files changed, 210 insertions(+), 209 deletions(-)
+
+commit ae75f066b573e9d29407c45af144ef69570e356b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:11:04 2022 -0600
+
+    [glyf] Split SubsetGlyph
+
+ src/Makefile.sources       |  1 +
+ src/OT/glyf/SubsetGlyph.hh | 70 ++++++++++++++++++++++++++++++++++++++++++++++
+ src/OT/glyf/glyf.hh        | 56 +------------------------------------
+ src/meson.build            |  1 +
+ 4 files changed, 73 insertions(+), 55 deletions(-)
+
+commit 0031069f47d0fd51c42b92f0d929196c17787c4d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:05:18 2022 -0600
+
+    [glyf] Fix includes
+
+ src/OT/glyf/Glyph.hh |  2 +-
+ src/OT/glyf/glyf.hh  | 16 ++++++++--------
+ src/OT/glyf/loca.hh  |  2 +-
+ 3 files changed, 10 insertions(+), 10 deletions(-)
+
+commit b4a0c30d98ff3bece104edf4a19a9ba65fa78b1b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 17:01:11 2022 -0600
+
+    [glyf] Remove hardcoded HB_MAX_COMPOSITE_OPERATIONS
+
+ src/hb-subset-plan.cc | 17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+commit 36373ee15c209ba6d49f4a36aaece91a3cff2f55
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 16:54:46 2022 -0600
+
+    [glyf] Move add_gid_and_children to subset-plan where it belongs
+
+ src/OT/glyf/glyf.hh   | 25 -------------------------
+ src/hb-subset-plan.cc | 31 ++++++++++++++++++++++++++++++-
+ 2 files changed, 30 insertions(+), 26 deletions(-)
+
+commit ba1c9eda3887d360b1585ae0dcf9bfc526ce8f7e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 16:51:33 2022 -0600
+
+    [glyf] Use a range for loop
+
+ src/OT/glyf/glyf.hh | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+commit e4f2bc93425f756ca5740a5d050a7521ee72465c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 16:38:50 2022 -0600
+
+    [glyf] Split Glyph.hh
+
+ src/Makefile.sources |    1 +
+ src/OT/glyf/Glyph.hh |  680 +++++++++++++++++++++++++
+ src/OT/glyf/glyf.hh  | 1368 +++++++++++++-------------------------------------
+ src/OT/glyf/loca.hh  |    1 +
+ src/meson.build      |    1 +
+ 5 files changed, 1037 insertions(+), 1014 deletions(-)
+
+commit 3f9c6bf3fcef9c2b0bc1e1d001440dcf76158b05
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 14:46:26 2022 -0600
+
+    [glyf] Minor in _write_loca()
+
+ src/OT/glyf/glyf.hh | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit 852985da0fb4686a2eb81b33f79f305fe4104425
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 14:41:51 2022 -0600
+
+    [glyf] Split loca.hh
+
+ src/Makefile.sources |  1 +
+ src/OT/glyf/glyf.hh  | 30 ++----------------------------
+ src/OT/glyf/loca.hh  | 42 ++++++++++++++++++++++++++++++++++++++++++
+ src/meson.build      |  1 +
+ 4 files changed, 46 insertions(+), 28 deletions(-)
+
+commit 100576b7b740cb65e808beba66d88f1c720715e5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 14:38:43 2022 -0600
+
+    [glyf] Start splitting file
+
+ src/Makefile.sources    |    1 +
+ src/OT/glyf/glyf.hh     | 1338 +++++++++++++++++++++++++++++++++++++++++++++++
+ src/hb-ot-glyf-table.hh | 1333 +---------------------------------------------
+ src/hb-subset-plan.cc   |    2 +-
+ src/meson.build         |    1 +
+ 5 files changed, 1342 insertions(+), 1333 deletions(-)
+
+commit e867ac3aefbd89a5f5da7f1740d0fb0ef532c8b5
+Merge: b1629b0ce 78c5ae397
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 12:43:04 2022 -0600
+
+    Merge pull request #3674 from harfbuzz/use-sinhala-no-hacks
+    
+    [use] Switch Sinhala to USE
+
+commit b1629b0ce000b561b2d643339747ae29e956b9d8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 25 11:51:31 2022 -0600
+
+    [gdef] Minor harmless use of HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED
+
+ src/hb-ot-layout-gdef-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 78c5ae3979d7915a32ffd355670b41db7d5fa91a
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Sat Jun 25 13:32:04 2022 -0400
+
+    [indic] Remove remnants of Sinhala
+
+ docs/features.dot                    |   4 +-
+ docs/usermanual-shaping-concepts.xml |   2 +-
+ docs/usermanual-what-is-harfbuzz.xml |   3 +-
+ src/gen-indic-table.py               |   5 -
+ src/hb-ot-shaper-indic-machine.hh    | 579 +++++++++++++++++------------------
+ src/hb-ot-shaper-indic-machine.rl    |   3 +-
+ src/hb-ot-shaper-indic-table.cc      |  68 ++--
+ src/hb-ot-shaper-indic.cc            | 206 ++++---------
+ src/hb-ot-shaper-indic.hh            |   5 +-
+ 9 files changed, 366 insertions(+), 509 deletions(-)
+
+commit 0cc948b96cfca4d1e1b02fe16f8e6d525a88e53a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Oct 1 12:23:39 2018 +0200
+
+    [use] Switch Sinhala to USE
+    
+    https://github.com/harfbuzz/harfbuzz/issues/1044
+
+ src/hb-ot-shaper.hh | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+commit 605982876918b16e20dab7d440a2b1c002fa8520
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Sat Jun 25 11:33:44 2022 -0400
+
+    [use] Reintroduce the HVM class for U+0DCA
+
+ src/gen-use-table.py            |   7 +-
+ src/hb-ot-shaper-use-machine.hh | 968 ++++++++++++++++++++++------------------
+ src/hb-ot-shaper-use-machine.rl |   5 +-
+ src/hb-ot-shaper-use-table.hh   |   4 +-
+ src/hb-ot-shaper-use.cc         |   2 +-
+ 5 files changed, 538 insertions(+), 448 deletions(-)
+
+commit 1555b3008197b77d142587322c0aec3fa83bc99f
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Fri Jun 24 21:02:26 2022 -0400
+
+    Add U+25CC to lone Robatic but not after U+17D9
+
+ src/gen-indic-table.py                             |   2 +
+ src/hb-ot-shaper-indic-table.cc                    |   6 +-
+ src/hb-ot-shaper-khmer-machine.hh                  | 266 +++++++++++----------
+ src/hb-ot-shaper-khmer-machine.rl                  |   2 +-
+ .../086d83239e8f958391ff6cdd8fda9376a4bd3673.ttf   | Bin 0 -> 1076 bytes
+ test/shape/data/in-house/tests/khmer-misc.tests    |   2 +
+ 6 files changed, 149 insertions(+), 129 deletions(-)
+
+commit 0f15cb12de7a3d5b1c8ae0820a33d7b60132c3f7
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Fri Jun 24 20:37:01 2022 -0400
+
+    [indic-table] Fix block headers
+
+ src/gen-indic-table.py          | 47 +++++++++++++++++++----------------------
+ src/hb-ot-shaper-indic-table.cc | 18 ++++++----------
+ 2 files changed, 28 insertions(+), 37 deletions(-)
+
+commit e35cfb4bdeed7eefc5d7e36ba2adfba52577ba20
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Fri Jun 24 20:18:10 2022 -0400
+
+    Document the subsetter argument of record-test.sh
+
+ test/shape/README.md | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 2674962cf5bdeea949fac5636c83acf63cbecac4
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Jun 24 21:00:54 2022 +0000
+
+    [repacker] Add comment to graph class.
+
+ src/graph/graph.hh | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+commit 81a2dd0e80710b08054234a89463efd5814d998a
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Jun 24 20:59:20 2022 +0000
+
+    [repacker] Update Makefile for repacker re-org.
+
+ src/Makefile.sources | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 7078560e330bad2aa4a027b1abc2f4186330b51e
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Jun 24 19:20:20 2022 +0000
+
+    [repacker] extract graph serialization code into a seperate file.
+
+ src/graph/graph.hh     | 113 +---------------------------------------------
+ src/graph/serialize.hh | 119 +++++++++++++++++++++++++++++++++++++++++++++++++
+ src/hb-repacker.hh     |  21 ++++-----
+ src/test-repacker.cc   |   6 +--
+ 4 files changed, 135 insertions(+), 124 deletions(-)
+
+commit 20b02a672d9f4c96f2db1b625f079cda5196450e
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Jun 24 18:58:17 2022 +0000
+
+    [repacker] Begin splitting up the repacker implementation into several files.
+
+ src/graph/graph.hh     |  965 ++++++++++++++++++++++++++++++++++++++++++++
+ src/graph/serialize.hh |  130 ++++++
+ src/hb-repacker.hh     | 1031 +-----------------------------------------------
+ src/test-repacker.cc   |    3 +-
+ 4 files changed, 1103 insertions(+), 1026 deletions(-)
+
+commit ad2ab1ddb42a16e02c6b16bb499bb8702d3d1654
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 24 11:08:35 2022 -0600
+
+    [indic] Clear syllables at the end of GSUB
+
+ src/hb-ot-shaper-indic.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 1f462804d1024b448df9281ab5648fb0027fe801
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 23 15:35:38 2022 -0600
+
+    [README.mingw.md] Add link to issue with further instructions
+
+ README.mingw.md | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+commit 8bfb3e9df2ed9832c970c14ebc1f69a812059616
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 23 13:28:07 2022 -0600
+
+    [indic] Disable vowel-constraints under uniscribe-bug-compatible
+
+ src/hb-ot-shaper-indic.cc | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit f8d052df6d97fcfec9d3cb317f1c16b7ba2a57fa
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 22 18:35:48 2022 -0600
+
+    [ansi-print] Remove impossible condition
+
+ util/ansi-print.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 251320ea222dbd9a6184f477d35829a4da1c0cf5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 22 16:44:07 2022 -0600
+
+    [ansi-print] Whitespace
+
+ util/ansi-print.hh | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 0d59d7952c334f6a3760a5b63456ede74dbf2351
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 22 16:39:36 2022 -0600
+
+    [ansi-print] Precision
+
+ util/ansi-print.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit c695a0915499061090a2c40360fbedb962a19eef
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 22 16:33:37 2022 -0600
+
+    [ansi-print] Reorder cases; harmless
+
+ util/ansi-print.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit ae0fce31cd6e92418e80dd197d989e3cb5b5eb71
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 22 16:31:21 2022 -0600
+
+    [ansi-print] Fix quadrants
+
+ util/ansi-print.hh | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 2ebaf0c5bac475f1e0f2d645ab44081c0e0663f6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 22 16:17:49 2022 -0600
+
+    [ansi-print] Fix unicolor detection
+
+ util/ansi-print.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit a4db80ca0a96a76dcba5b85b799c1858fafe7222
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 22 16:13:53 2022 -0600
+
+    [ansi-print] Fix color calculation
+    
+    Still something's wrong.
+
+ util/ansi-print.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 1abec5cd0da2a169d3bd6a512b2483f64b5b1be8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 21 13:39:16 2022 -0600
+
+    [CONFIG.md] Grammar
+
+ CONFIG.md | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+commit 0ab08a8bbb7ce288bd06a0718ba00e0cf58e8264
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 21 13:19:08 2022 -0600
+
+    [doap] Update
+
+ harfbuzz.doap | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit a5cf1a873845d39111e5eb14d084ca2112d03902
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 20 18:01:25 2022 -0600
+
+    Another null adjustment
+
+ src/hb-shape.cc  | 4 ++--
+ src/hb-shaper.cc | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+commit a7960bdfb00f055b821a1da96c6aad6563789646
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 17 15:10:20 2022 -0600
+
+    [config] Add HB_NO_LANGUAGE_LONG and enable in TINY profile
+    
+    Disables 3letter language tags and more complex ones.
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3664
+
+ src/gen-tag-table.py   |  8 ++++++--
+ src/hb-config.hh       |  1 +
+ src/hb-ot-tag-table.hh |  6 ++++--
+ src/hb-ot-tag.cc       | 19 +++++++++++++++++--
+ 4 files changed, 28 insertions(+), 6 deletions(-)
+
+commit 0d03123350404b5cf0b4865c4b0c7d740269cc13
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 20 16:51:35 2022 -0600
+
+    Mark a null variable as const
+
+ src/hb-shape.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 91d00ab722c1a07a0f5d880bcefd12307778fe74
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon Jun 20 13:36:01 2022 -0600
+
+    [ucd] Update
+
+ src/hb-ucd-table.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit a15ad778fede9e94428f6811293c71a63216234a
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Sun Jun 19 19:55:09 2022 +0200
+
+    [arabic-fallback] Generate PUA table from data
+    
+    Uses packtab for more compact arrays.
+
+ src/{ArabicPUA1.txt => ArabicPUASimplified.txt}  |   0
+ src/{ArabicPUA2.txt => ArabicPUATraditional.txt} |   0
+ src/gen-arabic-pua.py                            |  35 +++
+ src/hb-ot-cmap-table.hh                          |   6 +-
+ src/hb-ot-shaper-arabic-pua.hh                   | 383 +++++++----------------
+ 5 files changed, 144 insertions(+), 280 deletions(-)
+
+commit abc0685749e9e2dfc710773cc2a7a46c37b918bf
+Merge: 7ec4a556d 8c27c51c2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 19 11:05:17 2022 -0600
+
+    Merge pull request #3063 from harfbuzz/arabic-pua
+    
+    Arabic PUA shaping
+
+commit 7ec4a556d9addb1ad072ac4326659ec1a2900739
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 19 11:01:45 2022 -0600
+
+    [normalize] Cosmetic
+    
+    I didn't know this syntax is allowed in old C++.
+
+ src/hb-ot-shape-normalize.cc | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit 8c27c51c27c760a54350bf18ddfae34aaa19d89e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 19 10:47:38 2022 -0600
+
+    [arabic-pua] Rename symbols
+
+ src/hb-ft.cc                   |  4 ++--
+ src/hb-ot-cmap-table.hh        |  8 ++++----
+ src/hb-ot-shaper-arabic-pua.hh | 36 ++++++++++++++++++------------------
+ 3 files changed, 24 insertions(+), 24 deletions(-)
+
+commit 769896291176936d01c79a56bce3b33eb64e2776
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 19 10:41:45 2022 -0600
+
+    [arabic-fallback] Disable PUA shaping under HB_NO_OT_SHAPER_ARABIC_FALLBACK
+
+ src/hb-ft.cc            | 2 ++
+ src/hb-ot-cmap-table.hh | 2 ++
+ 2 files changed, 4 insertions(+)
+
+commit 4520911429dae90dc1342fadf1112967839e5899
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 19 10:36:24 2022 -0600
+
+    [arabic-fallback] Fix warning
+
+ src/hb-ot-shaper-arabic-fallback.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 55350377b0d26c06f152f0cd30c3911fd6060b85
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 19 10:13:31 2022 -0600
+
+    [cmap/ft] Only map 0xF000 range if font_page is NONE
+
+ src/hb-ft.cc            | 4 +++-
+ src/hb-ot-cmap-table.hh | 5 ++++-
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+
+commit 41a079bdec091e40e8afe36db4ef647cc3bde195
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 14:47:10 2022 -0600
+
+    [arabic-fallback] Make win1256 code build again
+    
+    Humm. Untested.
+
+ src/hb-ot-shaper-arabic-fallback.hh | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 1db6fddb24f6bb27d5d13890ea233859db7960a7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 14:34:46 2022 -0600
+
+    [arabic-fallback.hh] Hook up 3-letter ligatures
+
+ src/hb-null.hh                                               |  2 +-
+ src/hb-ot-shaper-arabic-fallback.hh                          | 10 ++++++----
+ test/shape/data/in-house/tests/arabic-fallback-shaping.tests |  2 +-
+ 3 files changed, 8 insertions(+), 6 deletions(-)
+
+commit 20e9f0b1d2efe86b632dcfda067244c578c0e20f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 14:12:30 2022 -0600
+
+    [arabic-fallback] Add the component loop
+    
+    Should be able to support 3-letter ligatures now.  Hooking up next.
+
+ src/hb-ot-shaper-arabic-fallback.hh | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+commit 9684d2d8aa86b5dade7a87f1cbea245904e20872
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 14:07:48 2022 -0600
+
+    [arabic-fallback] More baby steps
+
+ src/hb-ot-shaper-arabic-fallback.hh | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+commit 08715d75e07a3672e1e0e1159e58ec6a1c55d68e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 14:04:56 2022 -0600
+
+    [arabic-fallback] Another baby-step
+
+ src/hb-ot-shaper-arabic-fallback.hh | 19 ++++++++++++-------
+ 1 file changed, 12 insertions(+), 7 deletions(-)
+
+commit 15dd34b51587a8c00b449960e76d18d1add89ff6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 13:47:00 2022 -0600
+
+    [arabic-fallback] Another minor rename towards supporting 3-letter ligatures
+
+ src/hb-ot-shaper-arabic-fallback.hh | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+commit d86effa4a68138bb813fae056aebbb90d1af6b23
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 13:41:30 2022 -0600
+
+    [arabic-fallback] Rename; towards supporting 3-letter ligatures
+
+ src/gen-arabic-table.py             |  13 ++--
+ src/hb-ot-shaper-arabic-fallback.hh |   2 +-
+ src/hb-ot-shaper-arabic-table.hh    | 137 ++++++++++++++++++------------------
+ 3 files changed, 75 insertions(+), 77 deletions(-)
+
+commit 8978a18f31ca297e342cbac9caab486bbe2597d3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 13:25:46 2022 -0600
+
+    [arabick-fallback] Apply mark ligatures
+
+ test/shape/data/in-house/tests/arabic-fallback-shaping.tests | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 16c23713523f3c55bf24caabccb816becc8cc5af
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 13:25:46 2022 -0600
+
+    [arabick-fallback] Apply mark ligatures
+
+ src/hb-ot-shaper-arabic-fallback.hh | 28 +++++++++++++++++++++-------
+ 1 file changed, 21 insertions(+), 7 deletions(-)
+
+commit 7f362196c5b7ed2856e310f41999092e07d82281
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Sat Jun 18 20:28:43 2022 +0200
+
+    [arabic] Split ligature array
+    
+    Generate marks and 3-component ligatures in separate arrays. The new
+    arrays are unused currently.
+
+ src/gen-arabic-table.py          | 102 +++++++++++++++++++++++++++++++--------
+ src/hb-ot-shaper-arabic-table.hh |  42 +++++++++++++---
+ 2 files changed, 117 insertions(+), 27 deletions(-)
+
+commit 6e29060af2e4490e67e24ec2ef5172541275a855
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Sat Jun 18 15:34:40 2022 +0200
+
+    [arabic] Add mapping files for the PUA encoding
+    
+    Unused right now.
+
+ src/ArabicPUA1.txt | 250 +++++++++++++++++++++++++++++++++++++++++++++
+ src/ArabicPUA2.txt | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 545 insertions(+)
+
+commit dfc5e5a27dce6e95c523fcb6ad997f18e771801c
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Sat Jun 18 14:00:30 2022 +0200
+
+    [test] Skip glyph positions for test failing on CI
+    
+    We are not interested in glyph positioning for this test, and the
+    FreeType version on some CI machines is giving some different glyph
+    advances here.
+
+ test/shape/data/in-house/tests/arabic-fallback-shaping.tests | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 189e8c326e8421f2e59f10d9ab7355d2c3114147
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Sat Jun 18 13:28:54 2022 +0200
+
+    [ft] Remap legacy Arabic PUA codepoints
+    
+    Similar to what ot font finctions does, to support Support legacy
+    pre-OpenType Windows 3.1-era fonts.
+
+ src/hb-ft.cc                                       | 29 ++++++++++++++++------
+ .../in-house/tests/arabic-fallback-shaping.tests   | 20 +++++++--------
+ 2 files changed, 31 insertions(+), 18 deletions(-)
+
+commit 69cbd365cc0e5e5d90d510cc3407e4056d2a1734
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Sat Aug 14 02:39:46 2021 +0200
+
+    WIP: manually add data entries for PUA ligatures
+    
+    Just a test, I’m not entirely convinced we should do this.
+
+ src/gen-arabic-table.py          | 9 ++++++++-
+ src/hb-ot-shaper-arabic-table.hh | 1 +
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+commit c3f590bb1eb5e2451b80aa3d10a29c62b32b860d
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Thu Jun 16 11:04:13 2022 -0600
+
+    [arabic] Support legacy PUA shaping
+    
+    Support legacy pre-OpenType Windows 3.1-era fonts, by remapping PUA code
+    points in cmap table and letting our fallback shaper build the GSUB
+    table.
+    
+    Uniscribe applies also mset-like substitution, but our fallback mark
+    positioning gives better results, so this is not implemented.
+
+ src/Makefile.sources                               |   1 +
+ src/gen-arabic-table.py                            |  27 +-
+ src/hb-ot-cmap-table.hh                            |  45 +++-
+ src/hb-ot-os2-table.hh                             |   4 +-
+ src/hb-ot-shaper-arabic-pua.hh                     | 289 +++++++++++++++++++++
+ src/hb-ot-shaper-arabic-table.hh                   |  89 ++++++-
+ src/meson.build                                    |   1 +
+ test/shape/data/in-house/fonts/SimpArabicTest.ttf  | Bin 0 -> 17168 bytes
+ test/shape/data/in-house/fonts/TradArabicTest.ttf  | Bin 0 -> 58132 bytes
+ .../in-house/tests/arabic-fallback-shaping.tests   |  10 +
+ 10 files changed, 449 insertions(+), 17 deletions(-)
+
+commit b172f88c7d33cf2c563ca995104476cf0a876e05
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 18 14:00:27 2022 -0600
+
+    Fix a warning
+    
+    D:\a\harfbuzz\harfbuzz\src\hb-bit-set-invertible.hh(83): warning C4805: '^': unsafe mix of type 'uint32_t' and type 'const bool' in operation
+    [870/1075] Compiling C++ object src/harfbuzz.dll.p/hb-face.cc.obj
+    cl : Command line warning D9025 : overriding '/EHs' with '/EHs-'
+    cl : Command line warning D9025 : overriding '/EHc' with '/EHc-'
+
+ src/hb-bit-set-invertible.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit af74ab452f5796fb02d6a406a70033803781f17d
+Author: Garret Rieger <grieger@google.com>
+Date:   Thu Jun 16 18:12:09 2022 +0000
+
+    [repack] always run the sort in repack.
+    
+    This is needed to ensure virtual link ordering constraints are respected when repack is being called from fontTools. For subset usage, repack won't be called if the graph doesn't already overflow so this change doesn't cause any extra work.
+
+ src/hb-repacker.hh                   |   6 ++----
+ test/api/fonts/repacker_expected.otf | Bin 1400 -> 1400 bytes
+ 2 files changed, 2 insertions(+), 4 deletions(-)
+
+commit 29811a720c48d746c63c7109905b847425d29282
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 16 14:04:08 2022 -0600
+
+    Fix typo
+
+ src/check-symbols.py | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit c859cbfb88acf7e0063bbd4c2b576c153fbed17f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 16 13:55:12 2022 -0600
+
+    Mark an array as static
+    
+    Not sure how this wasn't flagged before.
+
+ src/hb-common.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 19802dfdf04da6565c869103298fc6deb6ac0ea6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 16 13:50:35 2022 -0600
+
+    Minor rename of static symbols to namespace them
+
+ src/hb-buffer-serialize.cc        |  8 ++++----
+ src/hb-ot-name-language-static.hh | 12 ++++++------
+ src/hb-shaper.cc                  | 12 ++++++------
+ 3 files changed, 16 insertions(+), 16 deletions(-)
+
+commit e0a5231657a6f09ca4afc93e1b2224eba7a0b544
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 16 13:24:02 2022 -0600
+
+    [draw] Lazy-allocate user-data/destroy callback vector
+
+ src/hb-draw.cc | 53 ++++++++++++++++++++++++++++++++++++++---------------
+ src/hb-draw.hh | 14 +++++++-------
+ 2 files changed, 45 insertions(+), 22 deletions(-)
+
+commit 823f32a0e2f242ba02fcf1db95361051ac374a49
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed Jun 15 18:50:45 2022 +0000
+
+    [subset] Fix potential out of bounds write setting overlap flag on composite glyphs.
+
+ src/hb-ot-glyf-table.hh | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit 3f9226da37e088e356fbd5bd00730062cd335ee4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 16 09:28:47 2022 -0600
+
+    [meta] Fix typo
+
+ src/hb-meta.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 1c0eeb7cb32ad071d95380dc3bc79952cbabd98d
+Author: Ryan VanderMeulen <rvandermeulen@mozilla.com>
+Date:   Wed Jun 15 23:05:15 2022 -0400
+
+    Don't try to set _USE_MATH_DEFINES if already defined
+
+ src/hb.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 25917c780f27d2d068f356b7a74f5eef22a83b3f
+Author: Ryan VanderMeulen <rvandermeulen@mozilla.com>
+Date:   Wed Jun 15 23:03:22 2022 -0400
+
+    Fix build warning when __GNUG__ isn't defined
+
+ src/hb-meta.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e9c0a74063b19e2cd6b333fad0c834e4f40a241c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 15 16:57:16 2022 -0600
+
+    Fix clang -Wcomma warnings
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3656
+
+ src/hb-bit-set-invertible.hh | 2 +-
+ src/hb-coretext.cc           | 4 ++--
+ src/hb-ot-var-fvar-table.hh  | 4 ++--
+ src/hb-repacker.hh           | 2 +-
+ src/hb-subset.cc             | 4 ++--
+ src/hb.hh                    | 1 +
+ 6 files changed, 9 insertions(+), 8 deletions(-)
+
+commit d9c18cc2f0bbd1b4e5c739665e80d8b48d01d33b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 15 16:50:34 2022 -0600
+
+    [indic-table] Update
+
+ src/hb-ot-shaper-indic-table.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 99a26bc19d7818adfba64502c41491606a89bba8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 15 16:14:31 2022 -0600
+
+    [indic-generator] Fix typo
+
+ src/gen-indic-table.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 2cbb7758665f2b98f72d1193f7a3343f13d44cee
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 08:57:21 2022 -0600
+
+    [myanmar] Fold category P into GB
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3649
+    
+    This actually now allows Asat after the Myanmar punctuation marks;
+    something I see in Wikipedia data.
+
+ src/gen-indic-table.py              |   4 +-
+ src/hb-ot-shaper-indic-table.cc     |  13 +-
+ src/hb-ot-shaper-myanmar-machine.hh | 620 ++++++++++++++++++------------------
+ src/hb-ot-shaper-myanmar-machine.rl |   7 +-
+ src/hb-ot-shaper-myanmar.cc         |   1 -
+ 5 files changed, 312 insertions(+), 333 deletions(-)
+
+commit b350e301863db05d1e1651bccc90f3447829c50e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 08:52:11 2022 -0600
+
+    [myanmar] Remove category D completely
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3651
+
+ src/gen-indic-table.py              |  2 --
+ src/hb-ot-shaper-myanmar-machine.hh | 29 ++++++++++++-----------------
+ src/hb-ot-shaper-myanmar-machine.rl |  6 +++---
+ 3 files changed, 15 insertions(+), 22 deletions(-)
+
+commit 8533214ac567145cfcdc54f59ec58b8ad0b749b6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 08:49:36 2022 -0600
+
+    [khmer] Fold category Coeng completely into category H
+
+ src/gen-indic-table.py              |  4 +---
+ src/hb-ot-shaper-indic-machine.hh   | 36 ++++++++++++++++++------------------
+ src/hb-ot-shaper-indic-table.cc     |  9 +++------
+ src/hb-ot-shaper-khmer-machine.hh   |  2 +-
+ src/hb-ot-shaper-khmer-machine.rl   |  8 +++++---
+ src/hb-ot-shaper-khmer.cc           |  2 +-
+ src/hb-ot-shaper-myanmar-machine.hh | 22 +++++++++++-----------
+ 7 files changed, 40 insertions(+), 43 deletions(-)
+
+commit 607a9fe793aa586d73fceae90424a3d3a45ad2b8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 04:20:23 2022 -0600
+
+    [indic-like] Remove category duplication
+
+ src/gen-indic-table.py              | 77 ++++++++++++++++++++++++++++++++++++-
+ src/hb-ot-shaper-indic-machine.hh   | 51 ++++++++++++------------
+ src/hb-ot-shaper-indic-machine.rl   |  5 +--
+ src/hb-ot-shaper-indic-table.cc     | 54 ++++++++++++++++++++++++++
+ src/hb-ot-shaper-indic.cc           | 23 -----------
+ src/hb-ot-shaper-indic.hh           | 61 -----------------------------
+ src/hb-ot-shaper-khmer-machine.hh   |  2 +-
+ src/hb-ot-shaper-khmer-machine.rl   |  4 +-
+ src/hb-ot-shaper-khmer.cc           | 23 -----------
+ src/hb-ot-shaper-myanmar-machine.hh |  2 +-
+ src/hb-ot-shaper-myanmar-machine.rl |  5 +--
+ src/hb-ot-shaper-myanmar.cc         | 35 -----------------
+ 12 files changed, 162 insertions(+), 180 deletions(-)
+
+commit 04851921951cde16121a8c6a913306f56dab77fe
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 03:55:23 2022 -0600
+
+    [indic-like] Remove dependence on ot_category_t
+
+ src/hb-ot-shaper-indic-machine.hh   | 2 +-
+ src/hb-ot-shaper-indic-machine.rl   | 2 +-
+ src/hb-ot-shaper-khmer-machine.hh   | 2 +-
+ src/hb-ot-shaper-khmer-machine.rl   | 2 +-
+ src/hb-ot-shaper-myanmar-machine.hh | 2 +-
+ src/hb-ot-shaper-myanmar-machine.rl | 2 +-
+ 6 files changed, 6 insertions(+), 6 deletions(-)
+
+commit 14049003ac3cf17631cc68ea78ba5989e248bd52
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 03:53:47 2022 -0600
+
+    [indic-like] Reduce indic-dependency of khmer/myanmar even more
+
+ src/hb-ot-shaper-indic-machine.hh   | 56 ++++++++++++++++++++-----------------
+ src/hb-ot-shaper-indic-machine.rl   |  4 +++
+ src/hb-ot-shaper-indic.hh           |  5 ----
+ src/hb-ot-shaper-khmer-machine.hh   |  2 +-
+ src/hb-ot-shaper-khmer-machine.rl   |  2 +-
+ src/hb-ot-shaper-myanmar-machine.hh |  4 +--
+ src/hb-ot-shaper-myanmar-machine.rl |  4 +--
+ src/hb-ot-shaper-myanmar.cc         |  2 +-
+ 8 files changed, 41 insertions(+), 38 deletions(-)
+
+commit a1c299da13008abcc119bdd44b6ed64a746efc54
+Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
+Date:   Mon Jun 13 10:50:59 2022 +0000
+
+    Bump actions/setup-python from 3 to 4
+    
+    Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4.
+    - [Release notes](https://github.com/actions/setup-python/releases)
+    - [Commits](https://github.com/actions/setup-python/compare/v3...v4)
+    
+    ---
+    updated-dependencies:
+    - dependency-name: actions/setup-python
+      dependency-type: direct:production
+      update-type: version-update:semver-major
+    ...
+    
+    Signed-off-by: dependabot[bot] <support@github.com>
+
+ .github/workflows/msvc-ci.yml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit f9f0969cb6d54f8ee43a28c6a454d8787d10a075
+Author: Garret Rieger <grieger@google.com>
+Date:   Mon Jun 13 17:55:26 2022 +0000
+
+    [subset] switch to hb_memcpy.
+
+ src/hb-ot-post-table-v2subset.hh | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit 1e34852f074e9d8afdd6b63590ef0b22bb0bf174
+Author: Garret Rieger <grieger@google.com>
+Date:   Sat Jun 11 01:27:33 2022 +0000
+
+    [subset] Fix undefined behaviour.
+    
+    Don't memcpy if there's nothing to copy.
+
+ src/hb-ot-post-table-v2subset.hh | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 311413f16b92a8d5811897b5793ca8a9f218b779
+Author: Garret Rieger <grieger@google.com>
+Date:   Sat Jun 11 01:05:57 2022 +0000
+
+    [subset] Fix fuzzer issue.
+    
+    Fixes https://oss-fuzz.com/testcase-detail/5693568490012672. new_index should be set from new_index2 when the entry is present in the map.
+
+ src/hb-ot-post-table-v2subset.hh                         |   7 ++++---
+ ...-testcase-minimized-hb-subset-fuzzer-5693568490012672 | Bin 0 -> 1543 bytes
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+commit 4ba7980b8eb2144f114054751c94c45fd8f61263
+Author: Matthias Clasen <mclasen@redhat.com>
+Date:   Sat Jun 11 15:50:37 2022 -0400
+
+    Fix the generated gobject headers
+    
+    These headers are not acceptable to modern compilers.
+    gcc says things like:
+    
+    /usr/include/harfbuzz/hb-gobject-enums.h:100:1: warning:
+      function declaration isn’t a prototype [-Wstrict-prototypes]
+      100 | hb_gobject_ot_metrics_tag_get_type () G_GNUC_CONST;
+
+ src/hb-gobject-enums.h.tmpl | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 79bb3b52787d8ef9b3bc1a83222068c702c01c1b
+Author: Ali Chraghi <63465728+alichraghi@users.noreply.github.com>
+Date:   Sat Jun 11 14:31:28 2022 +0430
+
+    [docs] fix typo
+
+ src/hb-buffer-serialize.cc | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit b5bdb9f955b7ccb5e212b29133e2718864bcb072
+Merge: 148283d0e 98116e5cf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 10:44:09 2022 +0100
+
+    Merge pull request #3648 from harfbuzz/indic-cleanup
+    
+    Indic cleanup
+
+commit 98116e5cf5f4f9b5cc2da634d3380defe9d55ab1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 11 03:42:36 2022 -0600
+
+    [myanmar] Fix comments on categories
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894955430
+
+ src/hb-ot-shaper-indic.hh           | 4 ++--
+ src/hb-ot-shaper-myanmar-machine.rl | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+commit 02016914b300692655d9967e31020a827623280b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 17:24:19 2022 -0600
+
+    [indic-generator] Remove unnecessary Myanmar category=D overrides
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894685106
+
+ src/gen-indic-table.py          | 21 +--------------------
+ src/hb-ot-shaper-indic-table.cc | 16 +++++++---------
+ 2 files changed, 8 insertions(+), 29 deletions(-)
+
+commit 937c878078869870520702006c286099f9167624
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 17:20:15 2022 -0600
+
+    [indic-generator] Remove unnecessary override for Myanmar U+1039
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894762535
+
+ src/gen-indic-table.py          | 2 --
+ src/hb-ot-shaper-indic-table.cc | 6 +++---
+ 2 files changed, 3 insertions(+), 5 deletions(-)
+
+commit 9504037ccb2391fc7e4041f9d35eb9a620052c67
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 17:13:16 2022 -0600
+
+    [indic-generator] Remove three unneeded Myanmar overrides U+AA74-6
+    
+    These three characters have Indic_Syllabic_Category=Consonant_Placeholder. The
+    original evidence that prompted these overrides says they can take tone marks.
+    They are not subjoined: Khamti Shan apparently does not use subjoined
+    characters at all. Therefore, PLACEHOLDER is good enough and these need not be
+    overridden to C.
+    
+    https://www.unicode.org/L2/L2008/08276-khamti-proposal.pdf
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894640713
+
+ src/gen-indic-table.py          | 5 -----
+ src/hb-ot-shaper-indic-table.cc | 6 +++---
+ 2 files changed, 3 insertions(+), 8 deletions(-)
+
+commit 02eb6606d7dfb8ad1d4dbdcad3badebc37a8dc49
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 17:10:42 2022 -0600
+
+    [indic-generator] Remove redundant PLACEHODER characters overrides
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894631922
+
+ src/gen-indic-table.py | 58 ++++++++++++++++++++++++--------------------------
+ 1 file changed, 28 insertions(+), 30 deletions(-)
+
+commit e16669ceacd417eaf348bb8653e5816b1d7f947a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 17:05:35 2022 -0600
+
+    [indic-generator] Remove redundant override of U+2010 / U+2011
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894630596
+
+ src/gen-indic-table.py | 3 ---
+ 1 file changed, 3 deletions(-)
+
+commit bb255cd9a6f42a982daada48b6069d96812eb35f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 17:03:52 2022 -0600
+
+    [indic-generator] Remove redundant override of U+0980
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894627064
+
+ src/gen-indic-table.py | 1 -
+ 1 file changed, 1 deletion(-)
+
+commit 30d8c87d018c05560c21f5c4ddda4ac817983292
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 16:56:15 2022 -0600
+
+    [myanmar] Document Medials
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894532676
+
+ src/hb-ot-shaper-indic.hh           | 8 ++++----
+ src/hb-ot-shaper-myanmar-machine.rl | 8 ++++----
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+commit e1826c371442851eb99628655a081d8bd78829ac
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 16:53:11 2022 -0600
+
+    [khmer] Reuse OT_H for OT_Coeng
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#discussion_r894517417
+
+ src/hb-ot-shaper-indic.hh         |   2 +-
+ src/hb-ot-shaper-khmer-machine.hh | 232 +++++++++++++++++++-------------------
+ src/hb-ot-shaper-khmer-machine.rl |   2 +-
+ 3 files changed, 119 insertions(+), 117 deletions(-)
+
+commit eb2f2e318ac2f43c0871f3b5a6101040602e0570
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 16:47:59 2022 -0600
+
+    [indic-generator] Update comment re U+104E
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3648#pullrequestreview-1002150048
+
+ src/gen-indic-table.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 0daafefdd193ed5f10454bf4eea5652a7d2eff4c
+Merge: 39c132a62 148283d0e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 10:55:49 2022 -0600
+
+    Merge branch 'main' into indic-cleanup
+
+commit 148283d0e060c00da2a661a3e7c86f824250ccec
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:48:39 2022 -0600
+
+    [syllabic] Use a buffer scratch-flag for has-broken-syllable
+
+ src/hb-buffer.hh                    |  1 +
+ src/hb-ot-shaper-indic-machine.hh   |  6 +++---
+ src/hb-ot-shaper-indic-machine.rl   |  2 +-
+ src/hb-ot-shaper-khmer-machine.hh   |  6 +++---
+ src/hb-ot-shaper-khmer-machine.rl   |  2 +-
+ src/hb-ot-shaper-myanmar-machine.hh |  4 ++--
+ src/hb-ot-shaper-myanmar-machine.rl |  2 +-
+ src/hb-ot-shaper-syllabic.cc        | 15 +--------------
+ src/hb-ot-shaper-use-machine.hh     |  4 ++--
+ src/hb-ot-shaper-use-machine.rl     |  2 +-
+ 10 files changed, 16 insertions(+), 28 deletions(-)
+
+commit 6997d10bc0cab13103b660eb968bc49e2a0fb0f7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:43:33 2022 -0600
+
+    [arabic] Remove a couple TODO items
+
+ src/hb-ot-shaper-arabic-fallback.hh | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit e1575f23473af32816e583f4f10e6ef8899dff90
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:41:39 2022 -0600
+
+    [iter] Remove a few TODO items
+
+ src/hb-iter.hh | 16 +++-------------
+ 1 file changed, 3 insertions(+), 13 deletions(-)
+
+commit 689c77530a69cefbe6f142842a135988a1454315
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:37:45 2022 -0600
+
+    [buffer] Actually remove TODO item
+    
+    In reality, one side is smaller and one side is larger. The existing code
+    handles that just fine.
+
+ src/hb-buffer.cc | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit d09e962b9f5cbb4866e1c51b2fc967b83e323b69
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:35:16 2022 -0600
+
+    [buffer] Update a TODO item
+
+ src/hb-buffer.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit b3f689e7e5a4272b52e4ee34fbac04e2723b2439
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:31:47 2022 -0600
+
+    [serializer] Remove TODO that's not gonna happen
+
+ src/hb-serialize.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 10a8cc28fc6455482b67cf1781fe72765826c4a5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:31:06 2022 -0600
+
+    [normalizer] Remove a TODO that's not going to happen
+
+ src/hb-ot-shape-normalize.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 7635568f29e8e9e32bc30f6a6c2651500991dde8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:29:15 2022 -0600
+
+    [font] Remove a TODO item that's not gonna happen
+
+ src/hb-font.hh | 1 -
+ 1 file changed, 1 deletion(-)
+
+commit bd453de7575ac808780e706734ca6dd4baa43660
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:25:48 2022 -0600
+
+    [unicode] Accept a couple hacks as permanent
+
+ src/hb-unicode.hh | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+commit 39c132a62d57d736971fe16e1e47817e443df4f0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:12:39 2022 -0600
+
+    [indic/myanmar] Simplify compare functions
+
+ src/hb-ot-shaper-indic.cc   | 2 +-
+ src/hb-ot-shaper-myanmar.cc | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit a5bcd8567fc42442510b3838b4be925fa1a45288
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 07:03:02 2022 -0600
+
+    [indic] Update comment re category listing
+
+ src/hb-ot-shaper-indic.hh | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+commit 10a5485136563e8bb2c7ca78b9d230483c36c682
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 06:34:56 2022 -0600
+
+    [indic/myanmar] Move is_consonant to .cc files
+
+ src/hb-ot-shaper-indic.cc   | 24 ++++++++++++++++++++++++
+ src/hb-ot-shaper-indic.hh   | 24 ------------------------
+ src/hb-ot-shaper-myanmar.cc | 27 ++++++++++++++++++++++++++-
+ 3 files changed, 50 insertions(+), 25 deletions(-)
+
+commit 1c657460efacb3848a4a719a1c97b40fb88b49fd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 06:29:45 2022 -0600
+
+    [indic] Expand MEDIAL_FLAGS
+
+ src/hb-ot-shaper-indic.cc | 2 +-
+ src/hb-ot-shaper-indic.hh | 4 +---
+ 2 files changed, 2 insertions(+), 4 deletions(-)
+
+commit 9e3917f6d61ced3771d6b4b9e74c20f5c40012b2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 06:20:56 2022 -0600
+
+    [indic] Move a couple of functions to .cc file
+
+ src/hb-ot-shaper-indic.cc | 14 ++++++++++++++
+ src/hb-ot-shaper-indic.hh | 14 --------------
+ 2 files changed, 14 insertions(+), 14 deletions(-)
+
+commit 165ef55e57194bb443b69830d3ddcbefa5d04e95
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 06:20:10 2022 -0600
+
+    [indic-generator] Move INDIC_COMBINE_CATEGORIES here
+
+ src/gen-indic-table.py          | 3 +++
+ src/hb-ot-shaper-indic-table.cc | 3 +++
+ src/hb-ot-shaper-indic.hh       | 2 --
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+commit b030dd9e8806cc35d01136d2cf371109e174b663
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 06:12:13 2022 -0600
+
+    [indic-table] Minor rename
+
+ src/gen-indic-table.py          |   4 +-
+ src/hb-ot-shaper-indic-table.cc | 194 ++++++++++++++++++++--------------------
+ 2 files changed, 99 insertions(+), 99 deletions(-)
+
+commit d414fb332811851c382d5120ae3055b468b33ede
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 10 00:50:47 2022 -0600
+
+    [indic/khmer/myanmar] Add static_assert that categories match OT_*
+
+ src/hb-ot-shaper-indic-machine.hh   | 36 ++++++++++++++++++------------------
+ src/hb-ot-shaper-indic-machine.rl   |  5 ++++-
+ src/hb-ot-shaper-indic.cc           | 28 +++++++++++++++++++++++-----
+ src/hb-ot-shaper-indic.hh           |  3 +++
+ src/hb-ot-shaper-khmer-machine.hh   | 22 +++++++++++-----------
+ src/hb-ot-shaper-khmer-machine.rl   |  5 ++++-
+ src/hb-ot-shaper-khmer.cc           | 24 ++++++++++++++++++++++++
+ src/hb-ot-shaper-myanmar-machine.hh | 22 +++++++++++-----------
+ src/hb-ot-shaper-myanmar-machine.rl |  4 +++-
+ src/hb-ot-shaper-myanmar.cc         | 36 ++++++++++++++++++++++++++++++++++++
+ 10 files changed, 137 insertions(+), 48 deletions(-)
+
+commit 15ea4ccb045838ff2fe880dbc01215c361ed9363
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 17:47:46 2022 -0600
+
+    [indic-like] Add note about replicated values
+
+ src/hb-ot-shaper-indic-machine.rl   | 1 +
+ src/hb-ot-shaper-khmer-machine.rl   | 1 +
+ src/hb-ot-shaper-myanmar-machine.rl | 1 +
+ 3 files changed, 3 insertions(+)
+
+commit 3289e815328114a39e19179ddbdf4a2ec2458fe5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 17:46:15 2022 -0600
+
+    [indic] Use categories from the machine
+
+ src/hb-ot-shaper-indic-machine.hh   | 60 ++++++++++++----------
+ src/hb-ot-shaper-indic-machine.rl   |  8 +++
+ src/hb-ot-shaper-indic.cc           | 99 +++++++++++++++++++++++++------------
+ src/hb-ot-shaper-indic.hh           | 54 +-------------------
+ src/hb-ot-shaper-khmer-machine.hh   |  2 +-
+ src/hb-ot-shaper-khmer-machine.rl   |  2 +-
+ src/hb-ot-shaper-myanmar-machine.hh |  4 +-
+ src/hb-ot-shaper-myanmar-machine.rl |  4 +-
+ 8 files changed, 118 insertions(+), 115 deletions(-)
+
+commit 37217fc9be1a60622ad16f0204b172a44d82e947
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 16:43:50 2022 -0600
+
+    [indic-generator/myanmar] Move most Myanmar category overrides to generator
+
+ src/gen-indic-table.py              | 110 +++++++++
+ src/hb-ot-shaper-indic-table.cc     | 120 ++++++----
+ src/hb-ot-shaper-indic.hh           |  19 +-
+ src/hb-ot-shaper-myanmar-machine.hh | 440 ++++++++++++++++++------------------
+ src/hb-ot-shaper-myanmar-machine.rl |  29 +--
+ src/hb-ot-shaper-myanmar.cc         |  94 +-------
+ 6 files changed, 443 insertions(+), 369 deletions(-)
+
+commit c136227f57b0ad42a2dcf8303e3d6df4c9c6280f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 13:36:19 2022 -0600
+
+    [indic-generator/khmer] Move Khmer overrides to generator
+
+ src/gen-indic-table.py          | 27 +++++++++++++++++++++++++++
+ src/hb-ot-shaper-indic-table.cc | 40 ++++++++++++++++++++++------------------
+ src/hb-ot-shaper-indic.hh       |  6 ++----
+ src/hb-ot-shaper-khmer.cc       | 38 +-------------------------------------
+ 4 files changed, 52 insertions(+), 59 deletions(-)
+
+commit 40aa4e8320204ec376e9b16f91da9c95ae82e6d9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 13:30:00 2022 -0600
+
+    [indic/khmer/myanmar] Add Khmer/Myanmar categories to indic_category_t
+
+ src/hb-ot-shaper-indic.hh           |  30 ++-
+ src/hb-ot-shaper-khmer-machine.hh   | 262 ++++++++++----------
+ src/hb-ot-shaper-khmer-machine.rl   |  18 +-
+ src/hb-ot-shaper-myanmar-machine.hh | 467 ++++++++++++++++++++----------------
+ src/hb-ot-shaper-myanmar-machine.rl |  47 ++--
+ 5 files changed, 454 insertions(+), 370 deletions(-)
+
+commit 25793075e2884a4462063e904b13d70edb449ff2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 13:11:46 2022 -0600
+
+    [indic-generator] Move Khmer/Myanmar vowel categories to the generator
+
+ src/gen-indic-table.py          | 16 +++++++++++++---
+ src/hb-ot-shaper-indic-table.cc | 42 ++++++++++++++++++++++++-----------------
+ src/hb-ot-shaper-khmer.cc       | 14 --------------
+ src/hb-ot-shaper-myanmar.cc     | 12 ------------
+ 4 files changed, 38 insertions(+), 46 deletions(-)
+
+commit a6c82d4b8c6bc130889b67bd53971be7ee513e4a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 13:04:28 2022 -0600
+
+    [myanmar] Simplify to not use position info from the indic table for shaping
+
+ src/hb-ot-shaper-myanmar.cc | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+commit 10cd8ac0e50b319c3838d05bd8e9c38ddf79beba
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 12:27:31 2022 -0600
+
+    [indic-generator] Move matra category overrides to generator
+
+ src/gen-indic-table.py          |  52 ++++++++++++
+ src/hb-ot-shaper-indic-table.cc | 178 +++++++++++++++++++++-------------------
+ src/hb-ot-shaper-indic.hh       |  61 --------------
+ src/hb-ot-shaper-khmer.cc       |   1 -
+ 4 files changed, 145 insertions(+), 147 deletions(-)
+
+commit c4e4f1d3874058439373f82d560855f967fb843d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 11:56:57 2022 -0600
+
+    [indic-generator] Move SMVD position overrides to generator
+
+ src/gen-indic-table.py          |  6 ++-
+ src/hb-ot-shaper-indic-table.cc | 88 +++++++++++++++++++++--------------------
+ src/hb-ot-shaper-indic.hh       |  4 --
+ 3 files changed, 50 insertions(+), 48 deletions(-)
+
+commit 2963154c155888072e441b0f9b5ecfe61593871e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 11:49:02 2022 -0600
+
+    [indic-generator] Add a couple comments
+
+ src/gen-indic-table.py    | 7 +++++--
+ src/hb-ot-shaper-indic.hh | 4 +++-
+ 2 files changed, 8 insertions(+), 3 deletions(-)
+
+commit 91d6f45bc97b3920c0b780d2d5b056486e0e1b3f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 07:34:44 2022 -0600
+
+    [indic-generator] Move some position overrides to the generator
+
+ src/gen-indic-table.py          |  35 +++-
+ src/hb-ot-shaper-indic-table.cc | 438 +++++++++++++++++-----------------------
+ src/hb-ot-shaper-indic.hh       |  14 +-
+ 3 files changed, 212 insertions(+), 275 deletions(-)
+
+commit 0ec4dcb93d56c72624a0030e274e8171b117bc8e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 07:33:43 2022 -0600
+
+    [indic-generator] Ouch
+    
+    Not sure how this was passing tests still.
+
+ src/gen-indic-table.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit f0269e0f1b58e481b65f23621065626f0c83eb97
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 07:10:47 2022 -0600
+
+    [indic-generator] Move Ra handling to the generator
+
+ src/gen-indic-table.py          | 14 +++++++
+ src/hb-ot-shaper-indic-table.cc | 88 +++++++++++++++++++++++++++++++++--------
+ src/hb-ot-shaper-indic.hh       | 30 +-------------
+ 3 files changed, 87 insertions(+), 45 deletions(-)
+
+commit 419d2146c27483ce91ca6ef2b1aec880a0bfdab7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 07:01:14 2022 -0600
+
+    [indic-generator] Cap off what categories have positions
+    
+    This was left off of the commit moving Indic categories to the generator.
+    It didn't fail any tests, but adding it back because it has implications
+    possibly.
+
+ src/gen-indic-table.py          |  7 +++++
+ src/hb-ot-shaper-indic-table.cc | 68 ++++++++++++++++++++---------------------
+ 2 files changed, 41 insertions(+), 34 deletions(-)
+
+commit e1d965d527a433fcb8e7b26451fdf77a1566f7e1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 06:48:25 2022 -0600
+
+    [indic-generator] Move position mapping to generator
+
+ src/gen-indic-table.py          |  38 ++-
+ src/hb-ot-shaper-indic-table.cc | 526 ++++++++++++++++++++--------------------
+ src/hb-ot-shaper-indic.hh       |  23 --
+ 3 files changed, 288 insertions(+), 299 deletions(-)
+
+commit 490751402686e86832019df0dfb0905b1a0b42d5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 06:33:51 2022 -0600
+
+    [indic-generator] Move category overrides to generator
+
+ src/gen-indic-table.py          |  96 +++++++++++++++++++++------
+ src/hb-ot-shaper-indic-table.cc | 144 +++++++++++++++++++++++++---------------
+ src/hb-ot-shaper-indic.hh       |  52 +--------------
+ 3 files changed, 169 insertions(+), 123 deletions(-)
+
+commit 58eeb3a180d03c9b39b39d99a6b9dbf30d17fd9f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 05:34:49 2022 -0600
+
+    [indic-generator] Move category mapping to generator
+
+ src/gen-indic-table.py          |  90 ++++++--
+ src/hb-ot-shaper-indic-table.cc | 492 ++++++++++++++++++----------------------
+ src/hb-ot-shaper-indic.hh       |  60 +----
+ 3 files changed, 305 insertions(+), 337 deletions(-)
+
+commit 899ca24387d84ebeff8ad6c9adbd72cd758b3aea
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 01:52:31 2022 -0600
+
+    [myanmar] Remove duplication of categories in the Myanmar shaper
+
+ src/Makefile.sources                |   1 -
+ src/hb-ot-shaper-myanmar-machine.hh |  50 ++++++----
+ src/hb-ot-shaper-myanmar-machine.rl |  40 +++++---
+ src/hb-ot-shaper-myanmar.cc         | 135 ++++++++++++++++++++++++---
+ src/hb-ot-shaper-myanmar.hh         | 177 ------------------------------------
+ src/meson.build                     |   1 -
+ 6 files changed, 181 insertions(+), 223 deletions(-)
+
+commit ce0528c0ff1bf14dc5741ea5cd8bd1e618e25310
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 01:34:10 2022 -0600
+
+    [khmer] Remove duplication of categories in the Khmer shaper
+
+ src/Makefile.sources              |   1 -
+ src/hb-ot-shaper-khmer-machine.hh |  48 +++++++++-------
+ src/hb-ot-shaper-khmer-machine.rl |  10 ++++
+ src/hb-ot-shaper-khmer.cc         |  72 ++++++++++++++++++++++--
+ src/hb-ot-shaper-khmer.hh         | 115 --------------------------------------
+ src/meson.build                   |   1 -
+ 6 files changed, 105 insertions(+), 142 deletions(-)
+
+commit 17c80035adc61c52a9e9600664496e3e4b837e37
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 9 04:17:38 2022 -0600
+
+    Revert "[cplusplus] Internally allow using hb_unique_ptr with hb_free()"
+    
+    This reverts commit b5f621b08d56c15832ab4f588a0673fce03305a0.
+    
+    A build was failing with clang 14 for Firefox apparently, sigh.
+    
+    https://github.com/harfbuzz/harfbuzz/issues/3647
+
+ src/hb.hh | 15 +--------------
+ 1 file changed, 1 insertion(+), 14 deletions(-)
+
+commit 9fc9b1ece4a0e7214456a27110e74e47ff3dbc15
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 12:35:43 2022 -0600
+
+    [layout] Minor, add using Lookup to GSUB/GPOS
+
+ src/OT/Layout/GSUB/GSUB.hh        | 2 ++
+ src/OT/Layout/GSUB/SubstLookup.hh | 2 +-
+ src/hb-ot-layout-gpos-table.hh    | 4 +++-
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+commit d4ddb3acf89f8539e56a742b30ed3cd424e043dd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 11:45:14 2022 -0600
+
+    Comments typos
+
+ src/hb-set-digest.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 4119f73c21f5fae7635d0c0658732b339711a76c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 06:34:48 2022 -0600
+
+    [subset/layout] Rename dispatch_closure_lookups_recurse_func to dispatch_recurse_func<>
+
+ src/OT/Layout/GSUB/SubstLookup.hh | 4 ----
+ src/hb-ot-layout-gpos-table.hh    | 7 +++----
+ src/hb-ot-layout-gsub-table.hh    | 4 +++-
+ src/hb-ot-layout-gsubgpos.hh      | 2 ++
+ 4 files changed, 8 insertions(+), 9 deletions(-)
+
+commit c13ff395201cf20af0f91c38f08908a1c1570b0b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 06:25:23 2022 -0600
+
+    [layout] Rename apply_recurse_func to specialization of dispatch_recurse_func
+
+ src/OT/Layout/GSUB/SubstLookup.hh | 2 --
+ src/hb-ot-layout-gpos-table.hh    | 5 ++---
+ src/hb-ot-layout-gsub-table.hh    | 3 ++-
+ src/hb-ot-layout.cc               | 2 +-
+ 4 files changed, 5 insertions(+), 7 deletions(-)
+
+commit 6a1edb8c9751bebd463664227d60e0218af18dd2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 11:38:17 2022 -0600
+
+    [set-digest] One more rename
+
+ src/hb-set-digest.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 6453737b0edac0624b141a8e2892deddfa7e3765
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 11:37:12 2022 -0600
+
+    [set-digest] Rename lowest_bits to bits_pattern
+
+ src/hb-set-digest.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit 2a061cb9cc86c43e62ed5234850ed38fd690a896
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 11:35:50 2022 -0600
+
+    [set-digest] Improve documentation
+
+ src/hb-set-digest.hh | 20 ++++++++++++++++----
+ 1 file changed, 16 insertions(+), 4 deletions(-)
+
+commit 9342adb0d6336fe56f1c28271db27d07c6ed7c3b
+Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
+Date:   Wed Jun 8 12:47:05 2022 +0000
+
+    Bump actions/checkout from 2 to 3
+    
+    Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
+    - [Release notes](https://github.com/actions/checkout/releases)
+    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
+    - [Commits](https://github.com/actions/checkout/compare/v2...v3)
+    
+    ---
+    updated-dependencies:
+    - dependency-name: actions/checkout
+      dependency-type: direct:production
+      update-type: version-update:semver-major
+    ...
+    
+    Signed-off-by: dependabot[bot] <support@github.com>
+
+ .github/workflows/configs-build.yml | 2 +-
+ .github/workflows/coverity-scan.yml | 2 +-
+ .github/workflows/linux-ci.yml      | 2 +-
+ .github/workflows/macos-ci.yml      | 2 +-
+ .github/workflows/msvc-ci.yml       | 2 +-
+ .github/workflows/msys2-ci.yml      | 2 +-
+ 6 files changed, 6 insertions(+), 6 deletions(-)
+
+commit 335d058bf4a61fa12373b375fcaff6fdd0aca55f
+Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
+Date:   Wed Jun 8 11:52:25 2022 +0000
+
+    Bump codecov/codecov-action from 1 to 3
+    
+    Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 3.
+    - [Release notes](https://github.com/codecov/codecov-action/releases)
+    - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
+    - [Commits](https://github.com/codecov/codecov-action/compare/v1...v3)
+    
+    ---
+    updated-dependencies:
+    - dependency-name: codecov/codecov-action
+      dependency-type: direct:production
+      update-type: version-update:semver-major
+    ...
+    
+    Signed-off-by: dependabot[bot] <support@github.com>
+
+ .github/workflows/linux-ci.yml | 2 +-
+ .github/workflows/macos-ci.yml | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit a51c3a66f2cb1a59f0d53d1b93060ebbd9048b80
+Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
+Date:   Wed Jun 8 11:52:27 2022 +0000
+
+    Bump actions/upload-artifact from 1 to 3
+    
+    Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 1 to 3.
+    - [Release notes](https://github.com/actions/upload-artifact/releases)
+    - [Commits](https://github.com/actions/upload-artifact/compare/v1...v3)
+    
+    ---
+    updated-dependencies:
+    - dependency-name: actions/upload-artifact
+      dependency-type: direct:production
+      update-type: version-update:semver-major
+    ...
+    
+    Signed-off-by: dependabot[bot] <support@github.com>
+
+ .github/workflows/cifuzz.yml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 80bf2b3d2f6dbe578c779d659d1a37610fa5b522
+Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
+Date:   Wed Jun 8 11:52:30 2022 +0000
+
+    Bump actions/setup-python from 1 to 3
+    
+    Bumps [actions/setup-python](https://github.com/actions/setup-python) from 1 to 3.
+    - [Release notes](https://github.com/actions/setup-python/releases)
+    - [Commits](https://github.com/actions/setup-python/compare/v1...v3)
+    
+    ---
+    updated-dependencies:
+    - dependency-name: actions/setup-python
+      dependency-type: direct:production
+      update-type: version-update:semver-major
+    ...
+    
+    Signed-off-by: dependabot[bot] <support@github.com>
+
+ .github/workflows/msvc-ci.yml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit acd21519272b60f686c2c9099d4ef34c4694cabc
+Author: naveen <172697+naveensrinivasan@users.noreply.github.com>
+Date:   Wed Jun 8 01:29:27 2022 +0000
+
+    chore: Included githubactions in the dependabot config
+    
+    This should help with keeping the GitHub actions updated on new releases. This will also help with keeping it secure.
+    
+    Dependabot helps in keeping the supply chain secure https://docs.github.com/en/code-security/dependabot
+    
+    GitHub actions up to date https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
+    
+    https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool
+    Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com>
+
+ .github/dependabot.yml | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+commit 42051fe18a4326bb844577f8c4bc11b58abf7c3a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 8 04:00:21 2022 -0600
+
+    [layout] s/inplace/always_inplace/g
+
+ src/hb-ot-layout.cc | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 21346af01d4222cdf50478fd4ad8b445d5e2f62e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 14:41:39 2022 -0600
+
+    [layout-cache] Adjust cost-function for recent change
+
+ src/hb-ot-layout-gsubgpos.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit bfee6839b0de03453653e35dce97300488463142
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 11:41:05 2022 -0600
+
+    [layout-cache] Cache lookahead, not input, classdef in ChainContextFormat2
+    
+    From the commit:
+    
+    +    /* For ChainContextFormat2 we cache the LookaheadClassDef instead of InputClassDef.
+    +     * The reason is that most heavy fonts want to identify a glyph in context and apply
+    +     * a lookup to it. In this scenario, the length of the input sequence is one, whereas
+    +     * the lookahead / backtrack are typically longer.  The one glyph in input sequence is
+    +     * looked-up below and no input glyph is looked up in individual rules, whereas the
+    +     * lookahead and backtrack glyphs are tried.  Since we match lookahead before backtrack,
+    +     * we should cache lookahead.  This decisions showed a 20% improvement in shaping of
+    +     * the Gulzar font.
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3636
+
+ src/hb-ot-layout-gsubgpos.hh | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+commit 39820af72f5632cc45ede44da2e2f0caa5df46a6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 10:18:38 2022 -0600
+
+    [layout] Add HB_NO_OT_LAYOUT_LOOKUP_CACHE to disable caching lookups
+    
+    Enabled when optimize-size profile is enabled.
+
+ src/hb-config.hh             |  4 ++++
+ src/hb-ot-layout-gsubgpos.hh | 48 ++++++++++++++++++++++++++++++++++++--------
+ 2 files changed, 44 insertions(+), 8 deletions(-)
+
+commit 845279c34ce38e72adc4573f6bb45ec0e657e673
+Merge: b59e25f25 c8fb048f7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 16:40:22 2022 +0100
+
+    Merge pull request #3636 from harfbuzz/classdef-cache
+    
+    Classdef cache
+
+commit b59e25f25ef20dddc7e4dff0432c63d1afe287ae
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 09:27:09 2022 -0600
+
+    [cff] Try fixing Heap-buffer-overflow in CFF::Charset::collect_glyph_to_sid_map
+    
+    Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47790
+
+ src/hb-ot-cff1-table.hh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit c8fb048f79964e0b6cdf9d322fc12c71328cfde8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 09:20:27 2022 -0600
+
+    [gsubgpos] Document caching
+
+ src/hb-ot-layout-gsubgpos.hh | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+commit 5963cf446907127c55fe0404e068c19ca7eb4490
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 09:12:45 2022 -0600
+
+    [gsubgpos] Merge cache_enter and cache_leave entry points
+    
+    Saves a pointer per subtable
+
+ src/hb-ot-layout-gsubgpos.hh | 102 ++++++++++++++++++++-----------------------
+ 1 file changed, 47 insertions(+), 55 deletions(-)
+
+commit d4c09e9a872967ebc2b9921ad1d267162e5ad569
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue Jun 7 09:03:30 2022 -0600
+
+    [gsubgpos] Remove apply_cached() entry point
+    
+    Just use a bool to apply()
+
+ src/hb-ot-layout-gsubgpos.hh | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+commit b96622d15c5e22ae214e4184142d28ee609293a4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 02:45:41 2022 -0600
+
+    [layout] Use a cache for main input ClassDef of (Chain)ContextLookupFormat2
+    
+    This commit adds a per-lookup caching infrastructure to GSUB/GPOS, and
+    uses it to cache input ClassDef.get_class value for (Chain)ContextLookupFormat2.
+    
+    For fonts heavy on use of heave class-based2 context matching, this shows
+    a good speedup. For NotoNastaliqUrdu for example, I observe 17% speedup.
+    
+    Unfortunately not many other lookups can use a cache like this :(.
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3636
+
+ src/hb-ot-layout-common.hh   |  13 ++
+ src/hb-ot-layout-gsubgpos.hh | 277 ++++++++++++++++++++++++++++++++++++-------
+ src/hb-ot-layout.cc          |  10 +-
+ 3 files changed, 257 insertions(+), 43 deletions(-)
+
+commit 356c1f833641c139b554548edbcd33bfbbc44540
+Author: neilnaveen <42328488+neilnaveen@users.noreply.github.com>
+Date:   Tue Jun 7 01:08:13 2022 +0000
+
+    chore: Set permissions for GitHub actions
+    
+     Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much.
+    
+    - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions
+    
+    https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+    
+    https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
+    
+    [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
+    
+    Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com>
+
+ .github/workflows/configs-build.yml | 3 +++
+ .github/workflows/coverity-scan.yml | 3 +++
+ .github/workflows/linux-ci.yml      | 3 +++
+ .github/workflows/macos-ci.yml      | 3 +++
+ .github/workflows/msvc-ci.yml       | 3 +++
+ .github/workflows/msys2-ci.yml      | 3 +++
+ 6 files changed, 18 insertions(+)
+
+commit 4266f4e29ada827cec1f38ab88ff2c071f6deb2f
+Author: Xavier Claessens <xavier.claessens@collabora.com>
+Date:   Fri Jun 3 12:06:56 2022 -0400
+
+    Fix check-* scripts when harfbuzz is a subproject
+    
+    When harfbuzz is a subproject paths are in the form
+    "subprojects/harfbuzz/src/...". Instead of removing "src/" prefix, take
+    the absolute path and make it relative to current source dir.
+    
+    This fix regression introduced in
+    https://github.com/harfbuzz/harfbuzz/pull/3394.
+
+ src/Makefile.am              |  1 +
+ src/check-c-linkage-decls.py | 16 +++++++++-------
+ src/check-header-guards.py   | 15 ++++++++-------
+ src/check-includes.py        | 15 ++++++++-------
+ src/meson.build              |  1 +
+ 5 files changed, 27 insertions(+), 21 deletions(-)
+
+commit 15543f70e04e726639c1b50ace6bdaa9c3ab50b6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 10:55:50 2022 -0600
+
+    [indic-like] Move allocation of syllable() buffer var to shapers that use it
+    
+    In indic, we don't have a pause location release the var.
+
+ src/hb-ot-layout.cc          | 1 -
+ src/hb-ot-layout.hh          | 3 ---
+ src/hb-ot-shaper-indic.cc    | 1 +
+ src/hb-ot-shaper-khmer.cc    | 3 ++-
+ src/hb-ot-shaper-myanmar.cc  | 2 ++
+ src/hb-ot-shaper-syllabic.cc | 8 ++++++++
+ src/hb-ot-shaper-syllabic.hh | 5 +++++
+ src/hb-ot-shaper-use.cc      | 2 ++
+ 8 files changed, 20 insertions(+), 5 deletions(-)
+
+commit 104dc85a2235cc14d2f40638c2f9fa00b39dc5a4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 06:56:35 2022 -0600
+
+    [buffer] Add try_allocate for buffer variables
+
+ src/hb-buffer.hh | 27 ++++++++++++++-------------
+ 1 file changed, 14 insertions(+), 13 deletions(-)
+
+commit b5f621b08d56c15832ab4f588a0673fce03305a0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 07:52:51 2022 -0600
+
+    [cplusplus] Internally allow using hb_unique_ptr with hb_free()
+    
+    ...for arbitrary types.
+
+ src/hb.hh | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+commit f18eb000d3884795a43ea46ce5faa95086267b2c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 04:17:05 2022 -0600
+
+    [buffer] Mark a variable as unused
+    
+    Weird. Somehow our HB_TINY bot started erring as this var being unused
+    in non-debug builds. Not sure why now...
+    
+        In file included from src/hb-ot-map.hh:32:0,
+                         from src/hb-ot-shape.hh:32,
+                         from src/hb-aat-layout.hh:32,
+                         from src/hb-aat-layout.cc:30,
+                         from src/harfbuzz.cc:1:
+        src/hb-buffer.hh: In member function ‘void hb_buffer_t::assert_var(unsigned int, unsigned int)’:
+        src/hb-buffer.hh:192:18: error: unused variable ‘bits’ [-Werror=unused-variable]
+             unsigned int bits = (1u<<end) - (1u<<start);
+                          ^~~~
+    cc1plus: some warnings being treated as errors
+    Error: Process completed with exit code 1.
+
+ src/hb-buffer.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e6409d3905d8801d1be647d505524f71230c6ca1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 06:57:37 2022 -0600
+
+    Revert "[layout] Use a cache for main input ClassDef of (Chain)ContextLookups"
+    
+    This reverts commit 57d1c08739d0acd94b96da2f9d5dd6c0ff3b3722.
+    
+    Err. This was an accident.
+
+ src/hb-ot-layout-common.hh   |  13 --
+ src/hb-ot-layout-gsubgpos.hh | 275 +++++++------------------------------------
+ src/hb-ot-layout.cc          |  10 +-
+ 3 files changed, 43 insertions(+), 255 deletions(-)
+
+commit 57d1c08739d0acd94b96da2f9d5dd6c0ff3b3722
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 02:45:41 2022 -0600
+
+    [layout] Use a cache for main input ClassDef of (Chain)ContextLookups
+
+ src/hb-ot-layout-common.hh   |  13 ++
+ src/hb-ot-layout-gsubgpos.hh | 275 ++++++++++++++++++++++++++++++++++++-------
+ src/hb-ot-layout.cc          |  10 +-
+ 3 files changed, 255 insertions(+), 43 deletions(-)
+
+commit 697287fbd7a8c8e21a36c9b73eeffecd6b862aaa
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 06:53:42 2022 -0600
+
+    [benchmark-shape,hb-shape-threads] Fix argument parsing order
+    
+    After recent change.
+
+ perf/benchmark-shape.cc          | 4 ++--
+ test/threads/hb-shape-threads.cc | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+commit c03a31417b5346b7b91ca1c2137a77625cd62c14
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 02:31:21 2022 -0600
+
+    [hb-shape-threads] Adjustments similar to benchmark-shape
+    
+    Duplication sighz.
+
+ test/threads/hb-shape-threads.cc | 34 ++++++++++++++++------------------
+ 1 file changed, 16 insertions(+), 18 deletions(-)
+
+commit d7c9cc34ae549ad6e54095732a6fc4aadfbadff4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 02:30:11 2022 -0600
+
+    Fix build
+
+ test/threads/hb-shape-threads.cc | 8 --------
+ 1 file changed, 8 deletions(-)
+
+commit 2dec74207067b793c5203fe7f8a00930f81d2f5a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 02:04:02 2022 -0600
+
+    [benchmark-shape] Remove a few tests
+    
+    They were not adding value.
+
+ perf/benchmark-shape.cc     | 12 ------------
+ perf/texts/fa-monologue.txt |  1 -
+ 2 files changed, 13 deletions(-)
+
+commit 3c258e1373e056cbddf27c31f1675547b884cfef
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 02:01:17 2022 -0600
+
+    [benchmark-shape] Reorder text vs font order for better output
+
+ perf/benchmark-shape.cc | 45 +++++++++++++++++++++++----------------------
+ 1 file changed, 23 insertions(+), 22 deletions(-)
+
+commit 92e81ab1dfcbd4e8ed4d70082a427c8e339269c9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 01:14:32 2022 -0600
+
+    [indic/khmer/myanmar] Shift category numbers around to avoid overlap
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3632
+
+ src/hb-ot-shaper-indic-machine.hh   | 433 +++++++++++++++++-------------------
+ src/hb-ot-shaper-indic-machine.rl   |  20 +-
+ src/hb-ot-shaper-indic.hh           |  21 +-
+ src/hb-ot-shaper-khmer-machine.hh   | 188 ++++++++--------
+ src/hb-ot-shaper-khmer-machine.rl   |   8 +-
+ src/hb-ot-shaper-myanmar-machine.hh | 298 ++++++++++++-------------
+ src/hb-ot-shaper-myanmar-machine.rl |   8 +-
+ 7 files changed, 481 insertions(+), 495 deletions(-)
+
+commit d6dbc0d17524e3f4bdaf7dbfd7f97b02e51fdb14
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 00:58:53 2022 -0600
+
+    [gi/ft] Silence warnings
+    
+    gi doesn't understand that FT_Face is a pointer, and not bare struct.
+    So it skips these APIs anyway. Mark skip to silense the warning.
+    
+    ../src/hb-ft.cc:242: Warning: HarfBuzz: hb_ft_font_get_face: return value: Invalid non-constant return of bare structure or union; register as boxed type or (skip)
+    ../src/hb-ft.cc:264: Warning: HarfBuzz: hb_ft_font_lock_face: return value: Invalid non-constant return of bare structure or union; register as boxed type or (skip)
+
+ src/hb-ft.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit f78a25098adc30ec24cde24676c2a077883a25e1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 00:55:35 2022 -0600
+
+    [gi] Remove Xconstructor annotations
+
+ src/hb-buffer.cc     | 2 +-
+ src/hb-draw.cc       | 2 +-
+ src/hb-face.cc       | 2 +-
+ src/hb-font.cc       | 4 ++--
+ src/hb-map.cc        | 2 +-
+ src/hb-set.cc        | 2 +-
+ src/hb-shape-plan.cc | 4 ++--
+ src/hb-unicode.cc    | 2 +-
+ 8 files changed, 10 insertions(+), 10 deletions(-)
+
+commit aef92b2846f08f99a30b2bd491b046c33576a884
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 00:44:31 2022 -0600
+
+    [ci] Better compiler specification in configs-build job
+
+ .github/workflows/configs-build.yml | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit cb961eac5ce98d43b0aa477b341b5bdd0b93fea2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Jun 5 00:44:07 2022 -0600
+
+    [configs] Fix builds with HB_NO_BUFFER_SERIALIZE but not HB_NO_BUFFER_VERIFY
+    
+    https://github.com/harfbuzz/harfbuzz/commit/e986c12075a69300a5e114fe139ae5acd762ef1b#commitcomment-75339317
+
+ src/hb-buffer-verify.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 67852504f61eda5721ebe3831d91cd6167922157
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 08:45:03 2022 -0600
+
+    [indic/myanmar] No-op update to machines
+
+ src/hb-ot-shaper-indic-machine.hh   |  49 +++---
+ src/hb-ot-shaper-indic-machine.rl   |   3 +-
+ src/hb-ot-shaper-indic.hh           |   5 +-
+ src/hb-ot-shaper-myanmar-machine.hh | 331 ++++++++++++++++++------------------
+ 4 files changed, 193 insertions(+), 195 deletions(-)
+
+commit 20a61ca447a952c54b18ecae273aa8639e6650d4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 08:35:22 2022 -0600
+
+    [myanmar] Fold D category into GB
+    
+    Trying to free a byte the buffer vars, for caching of lookups.
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3633
+
+ src/hb-ot-shaper-myanmar-machine.hh | 4 ++--
+ src/hb-ot-shaper-myanmar-machine.rl | 4 ++--
+ src/hb-ot-shaper-myanmar.hh         | 4 ++--
+ 3 files changed, 6 insertions(+), 6 deletions(-)
+
+commit f9b643f6b25ececbb6506dcc86eb5c50fd9824ca
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 07:29:40 2022 -0600
+
+    [layout] s/hb_get_subtables_context_t/hb_accelerate_subtables_context_t/g
+
+ src/hb-ot-layout-gsubgpos.hh | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+commit d4dfb8c1f19fe9ba99ff19718a67543fa027cb33
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 06:50:38 2022 -0600
+
+    [arabic] Free up buffer variable earlier
+
+ src/hb-ot-shaper-arabic.cc | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+commit 0c3d8c0d0f102c7ea622d94f86a24698206cb7a1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 06:05:23 2022 -0600
+
+    [README] Test adding as a symlink
+    
+    Autoconf requires README
+
+ README | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 4510d9a92894039a2ae3043b9b08a28b03c5ca66
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 06:02:25 2022 -0600
+
+    [README] Delete. We ship README.md
+
+ README | 15 ---------------
+ 1 file changed, 15 deletions(-)
+
+commit 01337e7a64d9c0fb907fc5aa5de495b84713ce9c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 06:01:52 2022 -0600
+
+    [README.md] minor
+
+ README.md | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 3972ec7340270d2f0385502ee8b9b13dd5b23ab7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 06:01:17 2022 -0600
+
+    [TESTING.md] Update profiling instructions.
+
+ TESTING.md | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+commit af41be6fa7e7a19f6d0e608b1d77b8ff4f4cef97
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Mon Apr 25 02:59:00 2022 +0200
+
+    Move freedesktop.org/wiki/HarfBuzz content to README.md
+
+ README.md | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+commit 9622337b524449257c43e70e8015627b4c6529dd
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Mon Apr 25 04:14:03 2022 +0200
+
+    Move old harfbuzz.org content to README.md
+    
+    Copied from:
+    
+      https://web.archive.org/web/20191221151847/https://freedesktop.org/wiki/Software/HarfBuzz/
+    
+    Edited lightly trying to merge it with existing README.md content and
+    remove grossly outdated bits. Might still need more editing.
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/1919
+
+ README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 55 insertions(+), 2 deletions(-)
+
+commit e2ece939fbc23fc8dbb44242680fbe682f9623ab
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 21:01:52 2022 +0200
+
+    [doc] Expand a little bit on Uniscribe compatibility
+    
+    https://github.com/harfbuzz/harfbuzz/issues/3556#issuecomment-1130247124
+
+ docs/usermanual-opentype-features.xml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 8d36300154d8fd774abebf8348cfdd6af971c50d
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 21:00:08 2022 +0200
+
+    [doc] Talk less about “complex” scripts
+    
+    Use more neutral terms and don’t make it like some scripts are outliers.
+
+ docs/usermanual-clusters.xml          |  2 +-
+ docs/usermanual-getting-started.xml   |  2 +-
+ docs/usermanual-opentype-features.xml |  4 ++--
+ docs/usermanual-shaping-concepts.xml  | 45 +++++++++++++++--------------------
+ 4 files changed, 23 insertions(+), 30 deletions(-)
+
+commit bd44840fab0532078e14201e7aad34614f981f81
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 20:24:32 2022 +0200
+
+    [doc] s/complexshapers/shapers/g
+
+ docs/features.dot | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit cc7ebb0ffa488a3e51efd13973ece4ddd8010dba
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat Jun 4 05:42:58 2022 -0600
+
+    Remove remaining mention to complex shapers in the code
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3628#issuecomment-1146248037
+
+ src/hb-ot-shape-normalize.cc | 2 +-
+ src/hb-ot-shape.cc           | 8 ++++----
+ src/hb-subset-input.cc       | 3 ++-
+ 3 files changed, 7 insertions(+), 6 deletions(-)
+
+commit b39b5f2f31d69d5fbe24659d294fd22f099f5956
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 04:10:28 2022 -0600
+
+    [name] Implement approximate language matching
+    
+    Very rudimentary.
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3354
+
+ src/hb-ot-name-table.hh | 36 +++++++++++++++++++++++++++++++-----
+ test/api/test-ot-name.c |  7 +++++++
+ 2 files changed, 38 insertions(+), 5 deletions(-)
+
+commit 40d7d56e53035e66db5a76a15be3fedd3f9f11cf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 05:40:18 2022 -0600
+
+    [subset-input] Minor move
+
+ src/hb-subset-input.cc | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 40193adbfcd4e33fa1fde2fa61b3bcca3548fd05
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 05:35:19 2022 -0600
+
+    [subset] Update default features list
+
+ src/hb-subset-input.cc | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+commit 26d8066a41fa75160429846e757a4989a7b54f12
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 10:28:02 2022 +0200
+
+    [test/shape] Remove texts subdirectory
+    
+    These texts were never hooked into the test suite, they however
+    represent a good collection of test texts, but one can always access
+    them from git history.
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3123
+
+ test/shape/Makefile.am                             |    1 -
+ .../script-arabic/language-persian/mehran.txt      |    8 -
+ .../language-urdu/crulp/ligatures/2grams.txt       |  601 --
+ .../language-urdu/crulp/ligatures/3grams.txt       | 3415 -----------
+ .../language-urdu/crulp/ligatures/4grams.txt       | 6316 --------------------
+ .../language-urdu/crulp/ligatures/5grams.txt       | 5029 ----------------
+ .../language-urdu/crulp/ligatures/6grams.txt       | 1542 -----
+ .../language-urdu/crulp/ligatures/7grams.txt       |  354 --
+ .../language-urdu/crulp/ligatures/8grams.txt       |   26 -
+ .../language-urdu/crulp/ligatures/LICENSE          |    3 -
+ .../language-urdu/crulp/ligatures/README           |   16 -
+ .../language-urdu/crulp/ligatures/SOURCES          |    4 -
+ .../script-arabic/misc/diacritics/lam-alef.txt     |   28 -
+ .../misc/diacritics/language-arabic.txt            |  695 ---
+ .../misc/diacritics/language-persian.txt           |   48 -
+ .../misc/diacritics/language-urdu.txt              |  188 -
+ .../misc/diacritics/ligature-components.txt        |   18 -
+ .../misc/diacritics/ligature-diacritics.txt        |    1 -
+ .../misc/diacritics/mark-skipping.txt              |   10 -
+ .../shaper-arabic/script-mongolian/misc/misc.txt   |    6 -
+ .../script-mongolian/misc/non-joining.txt          |    8 -
+ .../shaper-arabic/script-mongolian/misc/poem.txt   |    4 -
+ .../script-mongolian/misc/variation-selectors.txt  |    8 -
+ .../shaper-arabic/script-nko/misc/misc.txt         |    5 -
+ .../shaper-arabic/script-phags-pa/misc/misc.txt    |   14 -
+ .../script-syriac/misc/abbreviation-mark.txt       |   11 -
+ .../shaper-arabic/script-syriac/misc/alaph.txt     |   98 -
+ .../shaper-default/script-ethiopic/misc/misc.txt   |    1 -
+ .../shaper-default/script-han/misc/cjk-compat.txt  |    3 -
+ .../script-hiragana/misc/kazuraki-liga-lines.txt   |    8 -
+ .../script-hiragana/misc/kazuraki-liga.txt         |   53 -
+ .../shaper-default/script-linear-b/misc/misc.txt   |    1 -
+ .../shaper-default/script-tifinagh/misc/misc.txt   |   10 -
+ .../shaper-hangul/script-hangul/misc/misc.txt      |    4 -
+ .../script-hebrew/misc/diacritics.txt              |   16 -
+ .../shaper-indic/script-assamese/utrrs/LICENSE     |   19 -
+ .../shaper-indic/script-assamese/utrrs/README      |   13 -
+ .../shaper-indic/script-assamese/utrrs/SOURCES     |    2 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    4 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   40 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   10 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   11 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    6 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |   59 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt  |  131 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |  139 -
+ .../script-bengali/bengali-vowel-letters.txt       |    3 -
+ .../shaper-indic/script-bengali/misc/misc.txt      |   53 -
+ .../shaper-indic/script-bengali/misc/reph.txt      |   14 -
+ .../shaper-indic/script-bengali/utrrs/LICENSE      |   19 -
+ .../shaper-indic/script-bengali/utrrs/README       |   13 -
+ .../shaper-indic/script-bengali/utrrs/SOURCES      |    2 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    1 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   36 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   10 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   12 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    6 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |   58 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt  |  119 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |  215 -
+ .../devanagari-atomic-consonants.txt               |   33 -
+ .../script-devanagari/devanagari-vowel-letters.txt |   17 -
+ .../script-devanagari/misc/dottedcircle.txt        |    8 -
+ .../script-devanagari/misc/eyelash.txt             |    3 -
+ .../script-devanagari/misc/joiners.txt             |   19 -
+ .../shaper-indic/script-devanagari/misc/misc.txt   |   36 -
+ .../script-devanagari/misc/spec-deviations.txt     |    1 -
+ .../script-devanagari/misc/tricky-reordering.txt   |    5 -
+ .../shaper-indic/script-devanagari/utrrs/LICENSE   |   19 -
+ .../shaper-indic/script-devanagari/utrrs/README    |   13 -
+ .../shaper-indic/script-devanagari/utrrs/SOURCES   |    2 -
+ ...icFontFeatureCodepoint-AdditionalConsonants.txt |    8 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    4 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   45 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   14 -
+ ...tFeatureCodepoint-DevnagariSpecificAddition.txt |    1 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...ndicFontFeatureCodepoint-GenericPunctuation.txt |    2 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   16 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |   10 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |  185 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt  |  185 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            | 1367 -----
+ .../script-gujarati/gujarati-vowel-letters.txt     |    8 -
+ .../shaper-indic/script-gujarati/utrrs/LICENSE     |   19 -
+ .../shaper-indic/script-gujarati/utrrs/README      |   13 -
+ .../shaper-indic/script-gujarati/utrrs/SOURCES     |    2 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    1 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   34 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   12 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   13 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    7 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |  170 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt  |  170 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            | 1156 ----
+ .../script-gurmukhi/gurmukhi-vowel-letters.txt     |    9 -
+ .../shaper-indic/script-gurmukhi/misc/misc.txt     |    2 -
+ .../shaper-indic/script-gurmukhi/utrrs/LICENSE     |   19 -
+ .../shaper-indic/script-gurmukhi/utrrs/README      |   13 -
+ .../shaper-indic/script-gurmukhi/utrrs/SOURCES     |    2 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   38 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |    9 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ .../IndicFontFeatureCodepoint-GurmukhiSpecific.txt |    6 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   10 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    6 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |   22 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt  |    2 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |  152 -
+ .../script-kannada/kannada-vowel-letters.txt       |    3 -
+ .../shaper-indic/script-kannada/misc/misc.txt      |   20 -
+ .../script-kannada/misc/right-matras.txt           |    7 -
+ .../shaper-indic/script-kannada/utrrs/LICENSE      |   19 -
+ .../shaper-indic/script-kannada/utrrs/README       |   13 -
+ .../shaper-indic/script-kannada/utrrs/SOURCES      |    2 -
+ ...icFontFeatureCodepoint-AdditionalConsonants.txt |    1 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    4 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   40 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   13 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   14 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    9 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |  188 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |  306 -
+ .../script-malayalam/malayalam-vowel-letters.txt   |    5 -
+ .../shaper-indic/script-malayalam/misc/cibu.txt    |  188 -
+ .../script-malayalam/misc/dot-reph.txt             |   15 -
+ .../shaper-indic/script-malayalam/misc/misc.txt    |   65 -
+ .../shaper-indic/script-malayalam/utrrs/LICENSE    |   19 -
+ .../shaper-indic/script-malayalam/utrrs/README     |   13 -
+ .../shaper-indic/script-malayalam/utrrs/SOURCES    |    2 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    2 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   36 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   12 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   14 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    4 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |  254 -
+ .../shaper-indic/script-oriya/misc/bindu.txt       |    2 -
+ .../shaper-indic/script-oriya/misc/misc.txt        |   28 -
+ .../script-oriya/oriya-vowel-letters.txt           |    3 -
+ .../shaper-indic/script-oriya/utrrs/LICENSE        |   19 -
+ .../shaper-indic/script-oriya/utrrs/README         |   13 -
+ .../shaper-indic/script-oriya/utrrs/SOURCES        |    2 -
+ ...icFontFeatureCodepoint-AdditionalConsonants.txt |    3 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    2 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   34 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   12 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   12 -
+ .../IndicFontFeatureCodepoint-OriyaSpecific.txt    |    2 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    8 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |  170 -
+ .../shaper-indic/script-sinhala/misc/extensive.txt | 4390 --------------
+ .../shaper-indic/script-sinhala/misc/misc.txt      |   41 -
+ .../shaper-indic/script-sinhala/misc/reph.txt      |    3 -
+ .../script-sinhala/misc/split-matras.txt           |    4 -
+ .../shaper-indic/script-sinhala/utrrs/LICENSE      |   19 -
+ .../shaper-indic/script-sinhala/utrrs/README       |   13 -
+ .../shaper-indic/script-sinhala/utrrs/SOURCES      |    2 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   41 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   17 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   18 -
+ .../IndicFontFeatureCodepoint-Punctuation.txt      |    1 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    3 -
+ .../utrrs/gpos/IndicFontFeatureGPOS.txt            |  162 -
+ .../utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt   |    1 -
+ .../gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt    |   41 -
+ .../utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt     |   42 -
+ .../gsub/IndicFontFeatureGSUB-Special-Cases.txt    |    2 -
+ .../gsub/IndicFontFeatureGSUB-TouchingLetters.txt  |    1 -
+ .../utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt    |   41 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |    1 -
+ .../shaper-indic/script-tamil/misc/misc.txt        |   43 -
+ .../shaper-indic/script-tamil/utrrs/LICENSE        |   19 -
+ .../shaper-indic/script-tamil/utrrs/README         |   13 -
+ .../shaper-indic/script-tamil/utrrs/SOURCES        |    2 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   23 -
+ .../IndicFontFeatureCodepoint-CurrencySymbols.txt  |    1 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   11 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   12 -
+ .../IndicFontFeatureCodepoint-Numerics.txt         |    3 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-Symbols.txt          |    6 -
+ .../IndicFontFeatureCodepoint-TamilSymbol.txt      |    1 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    4 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |   64 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt  |   44 -
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |    4 -
+ .../shaper-indic/script-telugu/misc/misc.txt       |   12 -
+ .../script-telugu/telugu-vowel-letters.txt         |    5 -
+ .../shaper-indic/script-telugu/utrrs/LICENSE       |   19 -
+ .../shaper-indic/script-telugu/utrrs/README        |   13 -
+ .../shaper-indic/script-telugu/utrrs/SOURCES       |    2 -
+ .../IndicFontFeatureCodepoint-AdditionalVowels.txt |    2 -
+ .../IndicFontFeatureCodepoint-Consonants.txt       |   38 -
+ .../IndicFontFeatureCodepoint-DependentVowels.txt  |   13 -
+ .../codepoint/IndicFontFeatureCodepoint-Digits.txt |   10 -
+ ...IndicFontFeatureCodepoint-IndependentVowels.txt |   14 -
+ .../IndicFontFeatureCodepoint-Reserved.txt         |    2 -
+ .../IndicFontFeatureCodepoint-VariousSigns.txt     |    6 -
+ .../utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt  |  385 --
+ .../utrrs/gsub/IndicFontFeatureGSUB.txt            |  287 -
+ test/shape/texts/in-house/shaper-khmer/misc.txt    |   89 -
+ .../in-house/shaper-khmer/other-marks-invalid.txt  |    4 -
+ .../texts/in-house/shaper-khmer/other-marks.txt    |    7 -
+ .../shaper-myanmar/script-myanmar/misc/misc.txt    |    7 -
+ .../shaper-myanmar/script-myanmar/misc/otspec.txt  |    1 -
+ .../shaper-myanmar/script-myanmar/misc/utn11.txt   |   34 -
+ .../shaper-thai/script-lao/misc/sara-am.txt        |   20 -
+ .../in-house/shaper-thai/script-thai/misc/misc.txt |   11 -
+ .../shaper-thai/script-thai/misc/phinthu.txt       |   16 -
+ .../shaper-thai/script-thai/misc/pua-shaping.txt   |   11 -
+ .../shaper-thai/script-thai/misc/sara-am.txt       |   20 -
+ .../script-tibetan/misc/contractions.txt           |  612 --
+ .../shaper-tibetan/script-tibetan/misc/misc.txt    |    2 -
+ .../in-house/shaper-use/script-batak/misc.txt      |    9 -
+ .../in-house/shaper-use/script-buginese/misc.txt   |   70 -
+ .../texts/in-house/shaper-use/script-cham/misc.txt |    3 -
+ .../in-house/shaper-use/script-javanese/misc.txt   |   54 -
+ .../in-house/shaper-use/script-kaithi/misc.txt     |    6 -
+ .../in-house/shaper-use/script-kharoshti/misc.txt  |   36 -
+ .../in-house/shaper-use/script-tai-tham/misc.txt   |    2 -
+ .../shaper-use/script-tai-tham/torture.txt         |   23 -
+ 235 files changed, 32058 deletions(-)
+
+commit cd41557a96a57383266413818b065729b890d261
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 09:46:57 2022 +0200
+
+    [test/shape] Add PhagsPa tests
+    
+    Adopted from texts/in-house/shaper-arabic/script-phags-pa/misc/misc.txt.
+    
+    Using Noto Sans PhagsPa.
+
+ test/shape/data/in-house/Makefile.sources                |   1 +
+ .../fonts/ec404b8524cd56efa5d25524cc8541a0b6604b4f.ttf   | Bin 0 -> 8324 bytes
+ test/shape/data/in-house/meson.build                     |   1 +
+ test/shape/data/in-house/tests/arabic-phags-pa.tests     |  14 ++++++++++++++
+ 4 files changed, 16 insertions(+)
+
+commit 1ab12e314a64fac78d9c720cb6f786a1a3705228
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 09:37:25 2022 +0200
+
+    [test/shape] Add Malayalam dot-reph tests
+    
+    Adopted from texts/in-house/shaper-indic/script-malayalam/misc/dot-reph.txt.
+    
+    Using Noto Sans Malayalam.
+
+ test/shape/data/in-house/Makefile.sources                |   1 +
+ .../fonts/55e2910dbc9ef5dd89f4e146e7e0152169545b6a.ttf   | Bin 0 -> 5464 bytes
+ test/shape/data/in-house/meson.build                     |   1 +
+ .../data/in-house/tests/indic-malayalam-dot-reph.tests   |  15 +++++++++++++++
+ 4 files changed, 17 insertions(+)
+
+commit 6b2f3b5a99110fd8081f1e51cc5c3c1fcfbf7e2a
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 09:15:24 2022 +0200
+
+    [test/shape] Add Javanese tests
+    
+    Adopted from texts/in-house/shaper-use/script-javanese/misc.txt.
+    
+    I don’t know what font this was originally tested against, so I used
+    Noto Sans Javanese.
+
+ test/shape/data/in-house/Makefile.sources          |   1 +
+ .../f70f345188472b93f565d1d7fae8c668dd6a3244.ttf   | Bin 0 -> 90140 bytes
+ test/shape/data/in-house/meson.build               |   1 +
+ test/shape/data/in-house/tests/use-javanese.tests  |  54 +++++++++++++++++++++
+ 4 files changed, 56 insertions(+)
+
+commit c9c47dd8ad12cade0a5639086173bd3a3c23f4bd
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 08:58:57 2022 +0200
+
+    [test/shape] Add Hebrew diacritics tests
+    
+    Adopted from 406044986a68676f3050f9350ccc448c615fc685. Using
+    TaameyFrankCLM.ttf from the Mozilla issue.
+
+ test/shape/data/in-house/Makefile.sources          |   1 +
+ .../b895f8ff06493cc893ec44de380690ca0074edfa.ttf   | Bin 0 -> 29284 bytes
+ test/shape/data/in-house/meson.build               |   1 +
+ .../data/in-house/tests/hebrew-diacritics.tests    |  31 +++++++++++++++++++++
+ 4 files changed, 33 insertions(+)
+
+commit ef5d7febc826279e81aba84db8eb7ddf8289d2b0
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri Jun 3 08:46:30 2022 +0200
+
+    [test/shape] Allow using hb-subset in record-test.sh
+    
+    I think it is about time we use our own subsetter here. FontTools can
+    still be used.
+
+ test/shape/record-test.sh | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+commit 5bfb0b721c6c492ad61abf99bd36913cc83ec0b8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:56:41 2022 -0600
+
+    Rename s/shape-complex/shaper/g
+
+ src/Makefile.sources                               | 64 +++++++++++-----------
+ src/gen-indic-table.py                             |  2 +-
+ src/gen-use-table.py                               |  2 +-
+ src/gen-vowel-constraints.py                       |  2 +-
+ src/harfbuzz.cc                                    | 24 ++++----
+ src/hb-ot-shape-normalize.cc                       |  2 +-
+ src/hb-ot-shape.cc                                 |  2 +-
+ ...fallback.hh => hb-ot-shaper-arabic-fallback.hh} |  2 +-
+ ...list.hh => hb-ot-shaper-arabic-joining-list.hh} |  0
+ ...rabic-table.hh => hb-ot-shaper-arabic-table.hh} |  0
+ ...c-win1256.hh => hb-ot-shaper-arabic-win1256.hh} |  2 +-
+ ...pe-complex-arabic.cc => hb-ot-shaper-arabic.cc} |  6 +-
+ ...pe-complex-arabic.hh => hb-ot-shaper-arabic.hh} |  2 +-
+ ...-complex-default.cc => hb-ot-shaper-default.cc} |  2 +-
+ ...pe-complex-hangul.cc => hb-ot-shaper-hangul.cc} |  2 +-
+ ...pe-complex-hebrew.cc => hb-ot-shaper-hebrew.cc} |  2 +-
+ ...ic-machine.hh => hb-ot-shaper-indic-machine.hh} | 54 +++++++++---------
+ ...ic-machine.rl => hb-ot-shaper-indic-machine.rl} |  0
+ ...-indic-table.cc => hb-ot-shaper-indic-table.cc} |  2 +-
+ ...hape-complex-indic.cc => hb-ot-shaper-indic.cc} |  6 +-
+ ...hape-complex-indic.hh => hb-ot-shaper-indic.hh} |  2 +-
+ ...er-machine.hh => hb-ot-shaper-khmer-machine.hh} | 40 +++++++-------
+ ...er-machine.rl => hb-ot-shaper-khmer-machine.rl} |  0
+ ...hape-complex-khmer.cc => hb-ot-shaper-khmer.cc} |  4 +-
+ ...hape-complex-khmer.hh => hb-ot-shaper-khmer.hh} |  2 +-
+ ...-machine.hh => hb-ot-shaper-myanmar-machine.hh} | 40 +++++++-------
+ ...-machine.rl => hb-ot-shaper-myanmar-machine.rl} |  0
+ ...-complex-myanmar.cc => hb-ot-shaper-myanmar.cc} |  4 +-
+ ...-complex-myanmar.hh => hb-ot-shaper-myanmar.hh} |  2 +-
+ ...omplex-syllabic.cc => hb-ot-shaper-syllabic.cc} |  2 +-
+ ...omplex-syllabic.hh => hb-ot-shaper-syllabic.hh} |  2 +-
+ ...-shape-complex-thai.cc => hb-ot-shaper-thai.cc} |  2 +-
+ ...-use-machine.hh => hb-ot-shaper-use-machine.hh} | 54 +++++++++---------
+ ...-use-machine.rl => hb-ot-shaper-use-machine.rl} |  2 +-
+ ...plex-use-table.hh => hb-ot-shaper-use-table.hh} |  2 +-
+ ...ot-shape-complex-use.cc => hb-ot-shaper-use.cc} | 10 ++--
+ ...raints.cc => hb-ot-shaper-vowel-constraints.cc} |  2 +-
+ ...raints.hh => hb-ot-shaper-vowel-constraints.hh} |  2 +-
+ src/{hb-ot-shape-complex.hh => hb-ot-shaper.hh}    |  0
+ src/meson.build                                    | 64 +++++++++++-----------
+ src/update-unicode-tables.make                     | 20 +++----
+ 41 files changed, 217 insertions(+), 217 deletions(-)
+
+commit 44be1e5dfb8f7f9398d16421157ef363d1cae157
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:54:33 2022 -0600
+
+    s/SHAPE_COMPLEX/SHAPER/g
+
+ src/gen-arabic-joining-list.py                 | 6 +++---
+ src/gen-arabic-table.py                        | 6 +++---
+ src/gen-use-table.py                           | 6 +++---
+ src/gen-vowel-constraints.py                   | 2 +-
+ src/hb-config.hh                               | 8 ++++----
+ src/hb-ot-shape-complex-arabic-fallback.hh     | 6 +++---
+ src/hb-ot-shape-complex-arabic-joining-list.hh | 6 +++---
+ src/hb-ot-shape-complex-arabic-table.hh        | 6 +++---
+ src/hb-ot-shape-complex-arabic-win1256.hh      | 6 +++---
+ src/hb-ot-shape-complex-arabic.cc              | 2 +-
+ src/hb-ot-shape-complex-arabic.hh              | 6 +++---
+ src/hb-ot-shape-complex-hebrew.cc              | 2 +-
+ src/hb-ot-shape-complex-indic-machine.hh       | 6 +++---
+ src/hb-ot-shape-complex-indic-machine.rl       | 6 +++---
+ src/hb-ot-shape-complex-indic.hh               | 6 +++---
+ src/hb-ot-shape-complex-khmer-machine.hh       | 6 +++---
+ src/hb-ot-shape-complex-khmer-machine.rl       | 6 +++---
+ src/hb-ot-shape-complex-khmer.hh               | 6 +++---
+ src/hb-ot-shape-complex-myanmar-machine.hh     | 6 +++---
+ src/hb-ot-shape-complex-myanmar-machine.rl     | 6 +++---
+ src/hb-ot-shape-complex-myanmar.hh             | 6 +++---
+ src/hb-ot-shape-complex-syllabic.hh            | 6 +++---
+ src/hb-ot-shape-complex-thai.cc                | 2 +-
+ src/hb-ot-shape-complex-use-machine.hh         | 6 +++---
+ src/hb-ot-shape-complex-use-machine.rl         | 6 +++---
+ src/hb-ot-shape-complex-use-table.hh           | 6 +++---
+ src/hb-ot-shape-complex-vowel-constraints.cc   | 2 +-
+ src/hb-ot-shape-complex-vowel-constraints.hh   | 6 +++---
+ src/hb-ot-shape-complex.hh                     | 6 +++---
+ 29 files changed, 78 insertions(+), 78 deletions(-)
+
+commit 6fbb552156cc36e90ef25b0c6519a661bf76f597
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:50:25 2022 -0600
+
+    s/FLAG_COMPLEX/FLAG_SHAPER/g
+
+ src/hb-buffer.hh                  | 10 +++++-----
+ src/hb-ot-shape-complex-arabic.cc |  2 +-
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+commit 6d9e94d2b88915e6e672b0a937da3b89085b520a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:48:34 2022 -0600
+
+    s/hb_ot_shape_complex_categorize/hb_ot_shaper_categorize/g
+
+ src/hb-ot-shape-complex.hh | 2 +-
+ src/hb-ot-shape.cc         | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit a560182cb3b97d9484c6ab0697f99895c0109eb0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:46:58 2022 -0600
+
+    s/complex_var/ot_shaper_var/g
+
+ src/hb-ot-shape-complex-arabic.cc      | 2 +-
+ src/hb-ot-shape-complex-hangul.cc      | 2 +-
+ src/hb-ot-shape-complex-indic.hh       | 4 ++--
+ src/hb-ot-shape-complex-syllabic.cc    | 6 +++---
+ src/hb-ot-shape-complex-use-machine.hh | 2 +-
+ src/hb-ot-shape-complex-use-machine.rl | 2 +-
+ src/hb-ot-shape-complex.hh             | 6 +++---
+ 7 files changed, 12 insertions(+), 12 deletions(-)
+
+commit 13fbed29e484df26d51944b9e10d480449a9f0b1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:45:04 2022 -0600
+
+    s/HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS/HB_OT_SHAPE_MAX_COMBINING_MARKS/g
+
+ src/hb-ot-shape-complex-arabic.cc | 2 +-
+ src/hb-ot-shape-complex.hh        | 2 +-
+ src/hb-ot-shape-normalize.cc      | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+commit 44a7b3b773ad13bfa494aed266c7453996c41696
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:42:34 2022 -0600
+
+    s/ot_complex_shaper/ot_shaper/g
+
+ src/gen-vowel-constraints.py       |  2 +-
+ src/hb-ot-shape-complex-arabic.cc  |  2 +-
+ src/hb-ot-shape-complex-default.cc |  4 ++--
+ src/hb-ot-shape-complex-hangul.cc  |  2 +-
+ src/hb-ot-shape-complex-hebrew.cc  |  2 +-
+ src/hb-ot-shape-complex-indic.cc   |  2 +-
+ src/hb-ot-shape-complex-khmer.cc   |  2 +-
+ src/hb-ot-shape-complex-myanmar.cc |  4 ++--
+ src/hb-ot-shape-complex-thai.cc    |  2 +-
+ src/hb-ot-shape-complex-use.cc     |  2 +-
+ src/hb-ot-shape-complex.hh         | 36 ++++++++++++++++++------------------
+ src/hb-ot-shape.cc                 |  4 ++--
+ src/hb-ot-shape.hh                 |  4 ++--
+ 13 files changed, 34 insertions(+), 34 deletions(-)
+
+commit e5161977a40f2596af9d198565ccf2c4739300f9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:40:54 2022 -0600
+
+    s/COMPLEX_SHAPER/OT_SHAPER/g
+
+ src/hb-ot-shape-complex.hh | 30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+commit f3a8b7f36b29fa7a7c7946023cbdcb915e1d6cbf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:21:46 2022 -0600
+
+    [algs] Test hb_hash()
+
+ src/test-algs.cc | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+commit 7aacdd05bd4d6fa1305d6671521dd01d28b622c3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 02:10:06 2022 -0600
+
+    [cplusplus] Test hashing shared_ptr / unique_ptr
+
+ test/api/test-cplusplus.cc | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+commit 51ca1c9b59932899487f5c116cd33ce733929cfa
+Merge: 7ec3aad20 215a0afad
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 08:56:20 2022 +0100
+
+    Merge pull request #3626 from harfbuzz/fix-map
+    
+    Fix map
+
+commit 215a0afad19a43f88cb8fbeb51877997b40e2567
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 01:48:46 2022 -0600
+
+    [algs] Remove unused hb_coerce()
+
+ src/hb-algs.hh | 8 --------
+ 1 file changed, 8 deletions(-)
+
+commit 5dc12d7d8d7aeb3418870d2b3695ff10a53296f6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 01:37:02 2022 -0600
+
+    [cmap] Rewrite set_for() slightly
+
+ src/hb-ot-cmap-table.hh | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+commit 9552955e081f3d871765055fd5abad9070cfcf90
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 01:33:01 2022 -0600
+
+    Add an unlikely
+
+ src/hb-ot-cmap-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 88f00ecb84a5f78dffabdfa5a8bdc2ed1d452ce4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 01:30:27 2022 -0600
+
+    [map] Fix iter_ref () and test it
+
+ src/hb-map.hh   | 4 ++--
+ src/test-map.cc | 2 ++
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+commit a42c624fcaa030a68c51acaf007caf402c8c262c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 01:22:34 2022 -0600
+
+    Convert one final use of hashmap to unique_ptr
+
+ src/hb-ot-layout-gsubgpos.hh | 16 +++-------------
+ 1 file changed, 3 insertions(+), 13 deletions(-)
+
+commit f13a79548fca34663ec3f0f86de6f2e742a09ab9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 01:17:20 2022 -0600
+
+    [subset] Convert another use of hashmap to unique_ptr
+
+ src/hb-ot-layout-common.hh   | 14 +++++---------
+ src/hb-ot-layout-gsubgpos.hh |  2 +-
+ src/hb-subset-plan.cc        |  8 +-------
+ src/hb-subset-plan.hh        |  4 ++--
+ 4 files changed, 9 insertions(+), 19 deletions(-)
+
+commit 25f57230d58524d9165a9b33d1666f84005617d5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Jun 3 01:11:22 2022 -0600
+
+    [map] Return references from new iter_ref()
+
+ src/hb-map.hh | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+commit a7a688616ab348a66873df5577eec66a0f70206f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 18:59:15 2022 -0600
+
+    [cmap] Convert another map use to unique_ptr
+
+ src/hb-ot-cmap-table.hh | 13 ++-----------
+ 1 file changed, 2 insertions(+), 11 deletions(-)
+
+commit 997d9cc466abfb9031f46d1baef5a2cb3164f7cc
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 18:04:12 2022 -0600
+
+    [map] Make unique_ptr hashable
+
+ src/hb-bimap.hh                  |  3 ++-
+ src/hb-map.hh                    | 24 ++++++++++++------------
+ src/hb-ot-color-cpal-table.hh    |  4 ++--
+ src/hb-ot-layout-common.hh       |  4 ++--
+ src/hb-ot-layout-gsubgpos.hh     |  7 +++----
+ src/hb-ot-layout.cc              |  4 ++--
+ src/hb-ot-post-table-v2subset.hh |  4 +++-
+ src/hb-repacker.hh               | 16 ++++++++--------
+ src/test-map.cc                  |  8 ++++++++
+ 9 files changed, 42 insertions(+), 32 deletions(-)
+
+commit 8bb2a3326e92d553b9bea7462574a2d44782cbfd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 15:18:23 2022 -0600
+
+    [map] Remove unneeded assignment
+
+ src/hb-map.hh | 1 -
+ 1 file changed, 1 deletion(-)
+
+commit d7785a6da0a5ced69203270a48a9a4da9e8f230a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 12:42:24 2022 -0600
+
+    [cplusplus] Add unique_ptr
+
+ src/hb-algs.hh             |  5 +++++
+ src/hb-cplusplus.hh        | 44 +++++++++++++++++++++++++++++++++++++++++++-
+ test/api/test-cplusplus.cc |  2 ++
+ 3 files changed, 50 insertions(+), 1 deletion(-)
+
+commit bca710e8ad2cccaa013e19a63c58899e45284df8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 12:06:25 2022 -0600
+
+    [gsubgpos] Use map has() instead of get() when appropriate
+
+ src/hb-ot-layout-gsubgpos.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e9407a2bd25f80d65559b6a869585033e2a08b24
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 11:29:44 2022 -0600
+
+    Use shared_ptr<hb_set_t> in one place
+    
+    See if valgrind is happy...
+
+ src/hb-map.hh                |  2 +-
+ src/hb-ot-layout-gsubgpos.hh |  9 +++------
+ src/hb-ot-layout.cc          | 10 ++--------
+ 3 files changed, 6 insertions(+), 15 deletions(-)
+
+commit a42a703cb62c84b2ed141e64570e1f1a2d74695e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 11:51:20 2022 -0600
+
+    [shared_ptr] Clear p in destructor
+
+ src/hb-cplusplus.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit f0a0dcad703ca1db037687b4c59ce11668f38ca6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 11:25:56 2022 -0600
+
+    [test-map] Test hashing shared_ptr
+
+ src/test-map.cc | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+commit 4c1b5d9ece8b59eb5346a8eaeaad09dfeb8dfd7f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 11:25:11 2022 -0600
+
+    Whitespace
+
+ src/test-map.cc | 9 ---------
+ 1 file changed, 9 deletions(-)
+
+commit b9230c542558afac93f1fb6d7ca1442a06688d38
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 11:18:38 2022 -0600
+
+    [map] Fix has()
+
+ src/hb-map.hh | 21 +++++++++++++++++----
+ 1 file changed, 17 insertions(+), 4 deletions(-)
+
+commit 97ea10a63a0be388bfb02ae203c468533304bda0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 11:14:17 2022 -0600
+
+    Remove old nullptr_t hacks
+    
+    Were used for hashmap before.
+
+ src/hb-array.hh | 2 --
+ src/hb-map.hh   | 2 --
+ src/hb-set.hh   | 1 -
+ 3 files changed, 5 deletions(-)
+
+commit 3f78a71ca059b461f79d0ee64766c7c3f4327b14
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 11:11:35 2022 -0600
+
+    [map] Finally! Just can usd hb_hashmap_t<obj_t, obj_t>
+    
+    Yay!
+
+ src/hb-map.hh                    | 29 ++++++++++++++-------------
+ src/hb-ot-post-table-v2subset.hh |  2 +-
+ src/hb-serialize.hh              |  3 +--
+ src/test-map.cc                  | 43 ++++++++++------------------------------
+ 4 files changed, 27 insertions(+), 50 deletions(-)
+
+commit 0ccab339f98ab7af27b3ca0db8489ff836ca11f3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 10:43:36 2022 -0600
+
+    [map] Remove invalid-key template arguments since unused
+
+ src/hb-map.hh                    |  6 ------
+ src/hb-ot-post-table-v2subset.hh |  2 +-
+ src/hb-serialize.hh              |  3 +--
+ src/test-map.cc                  | 26 +++++++++++++-------------
+ 4 files changed, 15 insertions(+), 22 deletions(-)
+
+commit 3f6a8f69a099398ac397bb652e6d8332167f6538
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 10:36:07 2022 -0600
+
+    [map] Remove invalid-key special-casing
+    
+    Can override invalid-key value now.
+
+ src/hb-map.hh   |  3 ---
+ src/test-map.cc | 15 ++++++---------
+ 2 files changed, 6 insertions(+), 12 deletions(-)
+
+commit 5328b73fbaf5b952cf7eb23a7b4a228585502c10
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 10:32:56 2022 -0600
+
+    [map] Reduce map item size again
+
+ src/hb-map.hh | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit 4f58ae60eb781b9ade164c4ea2abad708d00f4ce
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 10:13:55 2022 -0600
+
+    [map] Keep is_used, is_tombstone as booleans
+
+ src/hb-map.hh | 44 ++++++++++++++++++++------------------------
+ 1 file changed, 20 insertions(+), 24 deletions(-)
+
+commit 7ec3aad20f04a51d7b3089374f3ea36b496eb8f5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 10:50:55 2022 -0600
+
+    [shared_ptr] Fix hb_hash() crash on nullptr
+
+ src/hb-algs.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 4d646773cf0fddb648119fbf575fbe3e5b6ab9fc
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 10:02:44 2022 -0600
+
+    [cplusplus] Make .reference() return T*
+
+ src/hb-cplusplus.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 7e7a4a8f05289552dbb217d20c4840efd43e31d2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 09:59:41 2022 -0600
+
+    [cplusplus] Fix build
+
+ src/hb-cplusplus.hh | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit a089d91fda71e4c19c9c3c822abe86bbcd878dbc
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 09:55:43 2022 -0600
+
+    [hash] Adjust hash for shared_ptr, implement it for std::hash
+
+ src/hb-algs.hh      | 12 ++++++------
+ src/hb-cplusplus.hh | 15 +++++++++++++--
+ 2 files changed, 19 insertions(+), 8 deletions(-)
+
+commit e037325efbfca23739e2b3265261c2528f52bae1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 08:51:12 2022 -0600
+
+    [hash] Remove custom hash, rely on std::hash
+
+ src/hb-algs.hh | 8 --------
+ 1 file changed, 8 deletions(-)
+
+commit 0d3d5b62ae9988695627db7f8b2d4fde044c8778
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Jun 2 08:00:08 2022 -0600
+
+    [cplusplus] Adjustments
+
+ src/hb-cplusplus.hh | 8 +++++++-
+ src/test-map.cc     | 4 +++-
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+commit 0b35940a72aaa4575e4dd1f8991abb037bc2a0ed
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 15:10:19 2022 -0600
+
+    Make hb::shared_ptr hashable
+
+ src/hb-algs.hh      | 16 +++++++++++-----
+ src/hb-cplusplus.hh |  1 +
+ src/test-map.cc     |  9 +++++++++
+ 3 files changed, 21 insertions(+), 5 deletions(-)
+
+commit 3817bdfd7f2747519024213aa0a26fdfdd27b293
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 12:35:03 2022 -0600
+
+    [hb.hh] Include hb-cplusplus.hh
+
+ src/hb.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit e0f3cab2466e3d47e16a18270b5026eae1daa807
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 11:51:43 2022 -0600
+
+    [cplusplus] Add hb-cplusplus.hh
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/2152
+
+ src/Makefile.sources       |   1 +
+ src/hb-cplusplus.hh        | 133 +++++++++++++++++++++++++++++++++++++++++++++
+ src/meson.build            |   1 +
+ src/test-map.cc            |   1 -
+ src/test-set.cc            |   2 -
+ test/api/test-c.c          |   2 +-
+ test/api/test-cplusplus.cc |  84 +++++++++++++++++++++++++++-
+ 7 files changed, 218 insertions(+), 6 deletions(-)
+
+commit 98aaecd3978c4389741789657d3fcacc8d1686d0
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed Jun 1 21:01:16 2022 +0000
+
+    [subset] fix data race touching Crap() in cff subsetting.
+
+ src/hb-ot-cff1-table.hh           | 3 ++-
+ src/hb-subset-cff-common.hh       | 3 ++-
+ test/threads/hb-subset-threads.cc | 2 +-
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+commit d8d96b26e74aa02aae6af96d35648981d5cea38d
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed Jun 1 19:55:02 2022 +0000
+
+    [threads-test] Add a threads test against hb-subset.
+
+ test/threads/Makefile.am          |   1 +
+ test/threads/hb-subset-threads.cc | 180 ++++++++++++++++++++++++++++++++++++++
+ test/threads/meson.build          |  15 ++++
+ 3 files changed, 196 insertions(+)
+
+commit 858570b1d9912a1b746ab39fbe62a646c4f7a5b1
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed Jun 1 18:08:09 2022 +0000
+
+    [subset] add some additional 32bit var store cases.
+    
+    Test the path where the 32 bit delta is not included.
+
+ .../32bit_var_store.notdef-outline-retain-gids.62.otf    | Bin 0 -> 3904 bytes
+ .../32bit_var_store.notdef-outline-retain-gids.63.otf    | Bin 0 -> 3828 bytes
+ .../32bit_var_store.notdef-outline-retain-gids.64.otf    | Bin 0 -> 3800 bytes
+ .../32bit_var_store.notdef-outline.62.otf                | Bin 0 -> 3892 bytes
+ .../32bit_var_store.notdef-outline.63.otf                | Bin 0 -> 3812 bytes
+ .../32bit_var_store.notdef-outline.64.otf                | Bin 0 -> 3780 bytes
+ test/subset/data/tests/32bit_var_store.tests             |   3 +++
+ 7 files changed, 3 insertions(+)
+
+commit 209d6aa2b789a757b2ead71e2a77d2e6c81f90c7
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed Jun 1 18:02:03 2022 +0000
+
+    [subset] Update make files for 32bit_var_store test.
+
+ test/subset/data/Makefile.am      | 1 +
+ test/subset/data/Makefile.sources | 1 +
+ test/subset/meson.build           | 1 +
+ 3 files changed, 3 insertions(+)
+
+commit 9c41bfe1a6119865dc94ff7142c7f8104063347c
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed Jun 1 17:53:14 2022 +0000
+
+    [subset] Add subset test of font with 32 bit delta in a var store.
+
+ ..._var_store.notdef-outline-retain-gids.61,62,63,64.otf | Bin 0 -> 5676 bytes
+ .../32bit_var_store.notdef-outline-retain-gids.61,62.otf | Bin 0 -> 4776 bytes
+ .../32bit_var_store.notdef-outline-retain-gids.61,63.otf | Bin 0 -> 4588 bytes
+ .../32bit_var_store.notdef-outline-retain-gids.61,64.otf | Bin 0 -> 4764 bytes
+ .../32bit_var_store.notdef-outline-retain-gids.61.otf    | Bin 0 -> 4128 bytes
+ .../32bit_var_store.notdef-outline.61,62,63,64.otf       | Bin 0 -> 5676 bytes
+ .../32bit_var_store.notdef-outline.61,62.otf             | Bin 0 -> 4776 bytes
+ .../32bit_var_store.notdef-outline.61,63.otf             | Bin 0 -> 4584 bytes
+ .../32bit_var_store.notdef-outline.61,64.otf             | Bin 0 -> 4744 bytes
+ .../32bit_var_store.notdef-outline.61.otf                | Bin 0 -> 4128 bytes
+ test/subset/data/fonts/32bit_var_store.otf               | Bin 0 -> 5664 bytes
+ test/subset/data/tests/32bit_var_store.tests             |  13 +++++++++++++
+ 12 files changed, 13 insertions(+)
+
+commit c88a6a9ec3c38793ec8b662362282e076e948943
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 09:46:41 2022 -0600
+
+    [face] Remove const from get_user_data prototype
+    
+    This was done by mistake.
+    
+    Since the returned user_data can be changed, face should not be marked
+    const. Other object types follow this parttern.
+
+ src/hb-face.cc | 2 +-
+ src/hb-face.h  | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit bc6ecaa2629d065583ffa86ebed4dcea53505f42
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 09:19:11 2022 -0600
+
+    [font-funcs] Handle case of null func but non-null destroy or user-data
+
+ src/hb-font.cc | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+commit 88ccbd2c4356b70107c212a81b5a99d08d2d3dd0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 08:44:07 2022 -0600
+
+    [font-funcs] Optimize user-data/destroy storage
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/2427
+
+ src/hb-font.cc | 74 ++++++++++++++++++++++++++++++++--------------------------
+ src/hb-font.hh | 40 +++++++++++++++----------------
+ 2 files changed, 61 insertions(+), 53 deletions(-)
+
+commit e421613e8f825508afa9a0b54d33085557c37441
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 09:07:57 2022 -0600
+
+    [sbix] Fix conditional
+
+ src/hb-ot-color-sbix-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 55b911d863a1aad9e0e00c1b91102f9614b56eec
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 08:13:06 2022 -0600
+
+    [buffer] Mark getter functions as taking const buffer
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/2873
+
+ src/hb-buffer.cc | 24 ++++++++++++------------
+ src/hb-buffer.h  | 24 ++++++++++++------------
+ 2 files changed, 24 insertions(+), 24 deletions(-)
+
+commit 18cd15bedefad709be80fd341549f46a1a8127bb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 07:55:59 2022 -0600
+
+    Rename test
+
+ test/threads/meson.build | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9e1479b5f041c48456aefeca93d3f885f84fb401
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 07:54:45 2022 -0600
+
+    [morx] Limit context length
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3097
+
+ src/hb-aat-layout-morx-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 62e803b36173fd096d7ad460dd1d1db9be542593
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 07:38:21 2022 -0600
+
+    [sbix] Limit glyph extents
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3557
+
+ src/hb-ot-color-sbix-table.hh       |   6 ++++++
+ test/fuzzing/fonts/sbix-extents.ttf | Bin 0 -> 582 bytes
+ 2 files changed, 6 insertions(+)
+
+commit cd05d187c893ad0bcf754393a865c417d5cff36d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 07:27:30 2022 -0600
+
+    [font] Fix undefined-behavior when scales are negative
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3555
+
+ src/hb-font.hh | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit fc4d42ff99018f9f640d3191bcd621c547ed61ea
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 05:19:23 2022 -0600
+
+    [ft] Add API to notify that hb_font_t changed
+    
+    New API:
+    - hb_ft_hb_font_changed()
+    
+    Mostly reverts 56e0ff9ea129aa91dfcc746cd61f8cbbc427dba7
+    
+    Related https://github.com/harfbuzz/harfbuzz/issues/2270
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3619
+
+ src/hb-ft.cc | 54 +++++++++++++++++++++++++++++++++---------------------
+ src/hb-ft.h  |  9 ++++++++-
+ 2 files changed, 41 insertions(+), 22 deletions(-)
+
+commit a31fd97c356cc49f9d4539567861b07c20bd034a
+Merge: 9c0c31dfa e246723f0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 12:26:08 2022 +0100
+
+    Merge pull request #3432 from harfbuzz/fuzz-verify
+    
+    [fuzz-shape] Verify shape output
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3432
+
+commit e246723f0c796ec5207e1b64dd7a409cebb91d99
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 04:54:18 2022 -0600
+
+    [shape] Fail shaping internally if buffer ops exceeded
+
+ src/hb-buffer.cc   |  2 ++
+ src/hb-ot-shape.cc |  2 --
+ src/hb-shape.cc    | 10 +++++++++-
+ 3 files changed, 11 insertions(+), 3 deletions(-)
+
+commit 5a058ba15837be53d8835031a689c22e369531b2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 05:35:17 2022 -0600
+
+    [shape-fuzzer] Add commented out more buffer-verify option
+    
+    Those currently fail and I've been unable to debug them.
+    
+    I tried two, passing them to hb-shape doesn't reproduce the failure. :(
+
+ test/fuzzing/hb-shape-fuzzer.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 189f65344a9c34618ecc11af30591165f8ff24d0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun Feb 13 13:22:08 2022 -0600
+
+    [fuzz-shape] Verify shape output
+    
+    Let the fuzzers loose on shape verify.
+
+ test/fuzzing/hb-shape-fuzzer.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9c0c31dfaab3dd3b3debb2604fec580ca6fdfb62
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 09:35:49 2022 -0600
+
+    [buffer] When deleting glyphs, check cluster backwards as well
+
+ src/hb-buffer.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 0384f80e78e79cdce2bb6a9f9bb08550bf0b95c5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 08:23:48 2022 -0600
+
+    [buffer-verify] If shaping buffers failed during verification, pass the test
+
+ src/hb-buffer-verify.cc | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+commit eba626ff6a2cbf92ddff267633ba1b7e5cb9540c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 08:20:56 2022 -0600
+
+    [shape-plan] Return empty plan if buffer direction is invalid
+    
+    Happens if buffer creation failed.
+
+ src/hb-shape-plan.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit a441c6c16b8f8355ce58543ad95621455dcb0824
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 07:52:04 2022 -0600
+
+    [shape] Only verify if text_buffer is successful
+
+ src/hb-shape.cc | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+commit f7f61aeb6fa9a9c9e62727f215d6fa4e55ddb546
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 09:37:38 2022 -0600
+
+    [buffer] Add TODO item
+
+ src/hb-buffer.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit d72d37008d25d346b73a24087202bbf957733121
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Jun 1 04:43:10 2022 -0600
+
+    [shape] Allow null buffer
+    
+    Ouch!
+
+ src/hb-shape.cc | 3 +++
+ 1 file changed, 3 insertions(+)
+
+commit 33145a4b75c1b31f657c379444aaa9c946b8fd61
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 04:59:07 2022 -0600
+
+    [test/shape] Pass --unsafe-to-concat to hb-shape
+
+ test/shape/run-tests.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 45a2252607740ae1612b5c2b03437c62cc6d221f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 06:23:47 2022 -0600
+
+    [flags] Fix undefined-behavior
+    
+    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../src/hb-buffer.hh:60:1 in
+    failure on clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5736539338833920
+
+ src/hb-algs.hh          |  2 +-
+ src/hb-buffer-verify.cc | 10 +++++-----
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+commit f3f9fc1544ba1a0d7d11dbd93d242f09b9349a0d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 06:05:57 2022 -0600
+
+    [buffer] Mark glyph_flags_t as flags
+
+ src/hb-buffer.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit ab143e85c377512365134c1904b8f9f668309438
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 06:00:41 2022 -0600
+
+    [buffer] Add HB_BUFFER_FLAG_DEFINED and HB_BUFFER_SERIALIZE_FLAG_DEFINED
+    
+    New API:
+    + HB_BUFFER_FLAG_DEFINED
+    + HB_BUFFER_SERIALIZE_FLAG_DEFINED
+
+ src/hb-buffer.h | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+commit 9a2a857043598fbc6826753e543e0a0c058dff35
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 04:25:20 2022 -0600
+
+    [ot-shape] Don't verify buffer if shaping failed
+    
+    Fixes all of fuzzing verify failures, which were result of buffer failure
+    on super-long results, which fails unsafe-to-break because shorter strings
+    don't fail shaping.
+
+ src/hb-buffer.cc             | 2 ++
+ src/hb-buffer.hh             | 1 +
+ src/hb-ot-layout-gsubgpos.hh | 3 +++
+ src/hb-shape.cc              | 4 +++-
+ 4 files changed, 9 insertions(+), 1 deletion(-)
+
+commit f8b26f43ece70d7b63f3d844db8059f682bdc50b
+Merge: 6dd7e31f7 5af5a5659
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 11:22:32 2022 +0100
+
+    Merge pull request #3606 from harfbuzz/32bit-varstore
+    
+    32bit varstore
+
+commit 6dd7e31f71e6c8aa9200f9af585c594db548e2fd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 31 04:09:06 2022 -0600
+
+    [util] Accept | as delimiter in Unicode parsing
+
+ util/text-options.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 5af5a565938505a4a549a5cee49b62ee03d73a18
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 08:32:50 2022 -0600
+
+    [VarStore] Implement writing 32bit var-store
+    
+    Untested.
+    
+    Finishes fixing https://github.com/harfbuzz/harfbuzz/issues/2965
+
+ src/hb-ot-layout-common.hh | 49 +++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 35 insertions(+), 14 deletions(-)
+
+commit 75112098ac3141416e8502779bf004cc5f7325a8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 12:42:15 2022 -0600
+
+    [VarStore] Implement reading 32bit var-store
+    
+    Untested.
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/2965
+    
+    Serializing is incomplete.
+
+ src/hb-ot-layout-common.hh | 68 ++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 53 insertions(+), 15 deletions(-)
+
+commit 334bd013d9c27907112df1b51da1431900fe288f
+Author: Xavier Claessens <xavier.claessens@collabora.com>
+Date:   Mon May 30 11:46:08 2022 -0400
+
+    Skip warning when building as subproject and  ragel is missing
+    
+    It is unlikely to be a developer build in that case.
+
+ src/meson.build | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit e5d6da79052d2ccabf423959f850ea90a9af7e6b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 12:25:39 2022 -0600
+
+    [varStore] rename shortCount to wordCount
+
+ src/hb-ot-layout-common.hh | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+commit d11455f2851dcf26c1300d88d12991d0988f115e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 06:59:03 2022 -0600
+
+    [blob] Fix strncpy() use in Mac resource opening code
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3616
+
+ src/hb-blob.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9eab6d326fc09d8957e082fb363cbe03b15feee0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 12:00:45 2022 -0600
+
+    [benchmark-set] Another Pause/Resume
+
+ perf/benchmark-set.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit ea2dd54b68db49adb05266ec18414b2bbc20af0f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 11:31:28 2022 -0600
+
+    [map] Place item hash between key and value, not after them
+    
+    This way if only one of key and value is 64bit (eg. pointer), and other is 32bit,
+    the whole item will fit in 128bit, whereas before it would have been bumped up to
+    196 if only value was 64bit (a common use-case for us.)
+
+ src/hb-map.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit ec6cefc46acec92322d08bf60ccd7585aac890bf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 11:26:37 2022 -0600
+
+    [repacker] Simplify map types
+
+ src/hb-repacker.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit cbcdf442c505b1461ef9591d1eedd849237a279b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 11:20:27 2022 -0600
+
+    [map] Speed up map's own hash()
+
+ src/hb-map.hh | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+commit de33ef61b7f8ce231bad8aa0e644142e2c23a633
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 11:07:21 2022 -0600
+
+    [map] Add TODO item
+
+ src/hb-map.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit fc5739ea901804f6b2eca643c8b30c5e1af4a2e5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 11:04:52 2022 -0600
+
+    [test-map] Whitespace
+
+ src/test-map.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 3e64abd5d6515d3f77b5bd3ca185c9161d5e3e17
+Merge: efa4385b1 b010962c3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:52:21 2022 -0600
+
+    Merge pull request #3613 from harfbuzz/threads-test
+    
+    Threads test
+
+commit efa4385b16a6f8881aaf40d4be1a4e894f8ee4c8
+Merge: f4a8b7001 342751198
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:40:56 2022 -0600
+
+    Merge pull request #3615 from harfbuzz/gir-freetype
+    
+    [gi] Add freetype2-2.0 for g-i-r includes
+
+commit b010962c3b9901e9b7a68d20c2ab5acb1653c925
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:34:25 2022 -0600
+
+    [test/hb-shape-threads] Silence hb_language_get_default() threadysafety issue
+
+ test/threads/hb-shape-threads.cc | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+commit f4a8b70016fa24cba8bee20f4aca0e2dd5a11c5c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:30:37 2022 -0600
+
+    More member initialization
+
+ src/hb-ot-layout-gsubgpos.hh | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+commit 4e59900ff5c14e09dd329bb42b5a7a566ad46d45
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:21:55 2022 -0600
+
+    [test/hb-shape-threads] Share font amongst threads
+
+ test/threads/hb-shape-threads.cc | 70 ++++++++++++++++++++--------------------
+ 1 file changed, 35 insertions(+), 35 deletions(-)
+
+commit 18b0bd0f5ff1d614ba8253b76619728d0aa28c6c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:12:12 2022 -0600
+
+    [test/hb-shape-threads] Verify buffer
+
+ test/threads/hb-shape-threads.cc | 1 +
+ test/threads/meson.build         | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+commit 484cc18732700aad614695aba271f70728bbebf9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:06:43 2022 -0600
+
+    [test-shape-threads] Set language
+
+ src/hb.hh                        | 1 -
+ test/threads/hb-shape-threads.cc | 5 +++++
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+commit f371789b40af052a33ad348b722a5c27f14b4c02
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 28 04:02:36 2022 -0600
+
+    Sprinkle static around
+
+ perf/benchmark-font.cc           | 4 ++--
+ perf/benchmark-shape.cc          | 4 ++--
+ test/threads/hb-shape-threads.cc | 8 ++++----
+ 3 files changed, 8 insertions(+), 8 deletions(-)
+
+commit 4386626ee072c5c8a7791040b041fac66f1b5dc0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 17:32:32 2022 -0600
+
+    [test/threads] Fix dependency
+
+ test/threads/meson.build | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit bf4b11cfa42f4f5906df412ed1ce0a93bc6b3ee9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 17:20:36 2022 -0600
+
+    [configure] Another try at fixing distcheck
+
+ configure.ac | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit e0544c481d6f218ebc04772e8e91c81a7cb8587c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 17:10:07 2022 -0600
+
+    [test/threads] Fix distcheck
+
+ test/Makefile.am         |  2 +-
+ test/threads/Makefile.am | 16 ++++++++++++++++
+ 2 files changed, 17 insertions(+), 1 deletion(-)
+
+commit e3d5a117a3e0f795a038b8e2c167399b4b662168
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 17:05:23 2022 -0600
+
+    [hb-shape-threads] Fix tsan race
+
+ test/threads/hb-shape-threads.cc | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+commit 33c990f0a9e3c0541f3673dfb4308153ff96decd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 16:57:00 2022 -0600
+
+    Include cassert to fix bots
+
+ perf/benchmark-font.cc           | 1 +
+ perf/benchmark-map.cc            | 1 +
+ perf/benchmark-set.cc            | 1 +
+ perf/benchmark-shape.cc          | 2 ++
+ perf/benchmark-subset.cc         | 1 +
+ test/threads/hb-shape-threads.cc | 1 +
+ 6 files changed, 7 insertions(+)
+
+commit 049af186840bf376c32b9cf786979c9f522afc6d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 16:53:25 2022 -0600
+
+    [threads] Add suite to test
+
+ test/threads/meson.build | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 87453f63faa710d46d3473d6c5419558d8b093fb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 16:51:12 2022 -0600
+
+    [hb-shape-threads] Fix current-work-dir so test passes
+
+ test/threads/meson.build | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit f77faf865480e049fbaf4ee2cf1f045870378836
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 16:34:28 2022 -0600
+
+    [hb-shape-threads] Allow overriding test parameters from cmdline
+
+ test/threads/hb-shape-threads.cc | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+commit f0fba59969d13a8639de3660007f720b00411a6c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 16:30:19 2022 -0600
+
+    [hb-shape-threads] Reduce num-iterations
+
+ test/threads/hb-shape-threads.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e8a2436332a38d2b69d54d0c39f55410d332780a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 16:29:17 2022 -0600
+
+    [threads] Add a condition_variable to test for all threads to be ready
+
+ test/threads/hb-shape-threads.cc | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+commit 4d42a94c19db547086d7d60cfcef548c018e5cbc
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 16:23:12 2022 -0600
+
+    [threads] Add hb-shape-threads test
+
+ test/meson.build                 |   1 +
+ test/threads/hb-shape-threads.cc | 185 +++++++++++++++++++++++++++++++++++++++
+ test/threads/meson.build         |   9 ++
+ 3 files changed, 195 insertions(+)
+
+commit 315ef83b4eae5fe7792df8c3959656fd7c3c0c5e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 30 05:09:26 2022 -0600
+
+    Revert "Revert "[ot-lang] Use atomic int for cache""
+    
+    This reverts commit c56ce8681c209abd147328142806769752091b1c.
+    
+    The revert was not intentional. Ouch!
+
+ src/hb-ot-tag.cc | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 34275119887bdf12d4f6ea87fcd55df34089f184
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Mon May 30 04:46:02 2022 +0200
+
+    [gi] Add freetype2-2.0 for g-i-r includes
+    
+    Fixes the warnings:
+    ../src/hb-ft.cc:810: Warning: HarfBuzz: hb_ft_face_create: argument ft_face: Unresolved type: 'FT_Face'
+    ../src/hb-ft.cc:886: Warning: HarfBuzz: hb_ft_face_create_cached: argument ft_face: Unresolved type: 'FT_Face'
+    ../src/hb-ft.cc:855: Warning: HarfBuzz: hb_ft_face_create_referenced: argument ft_face: Unresolved type: 'FT_Face'
+    ../src/hb-ft.cc:920: Warning: HarfBuzz: hb_ft_font_create: argument ft_face: Unresolved type: 'FT_Face'
+    ../src/hb-ft.cc:1029: Warning: HarfBuzz: hb_ft_font_create_referenced: argument ft_face: Unresolved type: 'FT_Face'
+    ../src/hb-ft.cc:240: Warning: HarfBuzz: hb_ft_font_get_face: return value: Unresolved type: 'FT_Face'
+    ../src/hb-ft.cc:262: Warning: HarfBuzz: hb_ft_font_lock_face: return value: Unresolved type: 'FT_Face'
+
+ src/Makefile.am | 2 +-
+ src/meson.build | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit 97aa1ce6ba7e5a1d2816600449b5b5406e618abb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun May 29 10:32:59 2022 -0600
+
+    [gsubgpos] Move some member initialization
+
+ src/hb-ot-layout-gsubgpos.hh | 33 ++++++++++-----------------------
+ 1 file changed, 10 insertions(+), 23 deletions(-)
+
+commit 0bb4c1f021b0c9d0985e61e9596757e57298f144
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun May 29 10:23:19 2022 -0600
+
+    [cache] Set default values for cache template parameters
+
+ src/hb-cache.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 3957d2927d670d176ca90ec7a7aa6e86822fbec3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun May 29 07:30:58 2022 -0600
+
+    [layout] Remove stale comment
+
+ src/hb-ot-layout.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit b6fed6f7116b258c6bd76024064a0d856d3ccd97
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sun May 29 06:33:34 2022 -0600
+
+    [set-digest] Minor don't use !! when auto bool conversion happens
+
+ src/hb-set-digest.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 371e14d99c6c84e11d71957c55b536530db1e415
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 28 13:40:30 2022 -0600
+
+    Combine uses of map has() then get() with has(.., &..)
+
+ src/hb-ot-color-cpal-table.hh    |  5 +++--
+ src/hb-ot-layout-common.hh       |  3 ++-
+ src/hb-ot-post-table-v2subset.hh |  3 +--
+ src/hb-repacker.hh               | 18 +++++++++++-------
+ 4 files changed, 17 insertions(+), 12 deletions(-)
+
+commit b99efa6c8dcfe7ea2c0804a77fbb6a485d38e664
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 28 05:16:34 2022 -0600
+
+    [map] Minor: use const reference in has()
+
+ src/hb-map.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 24d5a11dcb767d1c24fbdbd6d6779422e36ee794
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 28 05:14:16 2022 -0600
+
+    [bimap] Add unlikely and minor optimization in is_empty()
+
+ src/hb-bimap.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit c56ce8681c209abd147328142806769752091b1c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 28 04:25:51 2022 -0600
+
+    Revert "[ot-lang] Use atomic int for cache"
+    
+    This reverts commit d61b2074915cf5f8044dcb8e3dafc04b5b58c6b8.
+
+ src/hb-ot-tag.cc | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 63bc6be0cf7151dfbd7a004f5b644e802c2ff6ca
+Merge: e2aa29907 a719e6788
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 08:25:22 2022 -0600
+
+    Merge pull request #3603 from harfbuzz/font-serial
+    
+    Add font serial API
+
+commit e2aa29907dd172fdd779fe34b4ecd9893eac3391
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 27 07:06:02 2022 -0600
+
+    [set] Use relaxed atomic ops for last_page_index
+    
+    Since iterating a set from multiple threads is supported.
+
+ src/hb-bit-set.hh | 32 ++++++++++++++++----------------
+ 1 file changed, 16 insertions(+), 16 deletions(-)
+
+commit d61b2074915cf5f8044dcb8e3dafc04b5b58c6b8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 18:24:43 2022 -0600
+
+    [ot-lang] Use atomic int for cache
+    
+    Fixes(?) https://github.com/harfbuzz/harfbuzz/issues/3612
+
+ src/hb-ot-tag.cc | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 67bd147c73b3b01a62dd010f8081129b9ddb1ef5
+Merge: 0fe186922 e00c7358a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 05:16:07 2022 -0600
+
+    Merge pull request #3610 from googlefonts/subset_create_tables_face
+    
+    [subset] fix subsetting of faces created via hb_face_create_for_tables.
+
+commit a719e67887ccd5659aab0ba1fc6ff819795f7aa7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 17:51:24 2022 -0600
+
+    [ot-font] Use atomic ops for cache serial number
+    
+    This guarantees the cache is coherent.
+
+ src/hb-ot-font.cc | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 5248b2567b9f627097ad25afd9671da9c9997224
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 13:55:17 2022 -0600
+
+    [ot-font/h-advance] Adjust varStore cache condition
+    
+    This gives best performance for short strings, now that we have a h-advance cache as well.
+    The en-words benchmark in particular, now ot-font is faster than ft.
+    
+    Second to last line is of interest:
+    
+    Before:
+    -----------------------------------------------------------------------------------------------------
+    Benchmark                                                           Time             CPU   Iterations
+    -----------------------------------------------------------------------------------------------------
+    BM_Shape/en-words.txt/Roboto-Regular.ttf/hb                      29.8 ms         29.8 ms           23
+    BM_Shape/en-words.txt/Roboto-Regular.ttf/ft                      30.4 ms         30.4 ms           23
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/hb           16.3 ms         16.3 ms           43
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/ft           16.5 ms         16.5 ms           42
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/var/hb       18.0 ms         18.0 ms           39
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/var/ft       17.8 ms         17.8 ms           39
+    
+    After:
+    behdad@Behdads-MacBook-Pro harfbuzz % ninja -Cbuild && build/perf/benchmark-shape --benchmark_filter=en-words
+    -----------------------------------------------------------------------------------------------------
+    Benchmark                                                           Time             CPU   Iterations
+    -----------------------------------------------------------------------------------------------------
+    BM_Shape/en-words.txt/Roboto-Regular.ttf/hb                      30.0 ms         30.0 ms           23
+    BM_Shape/en-words.txt/Roboto-Regular.ttf/ft                      30.3 ms         30.3 ms           23
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/hb           16.3 ms         16.3 ms           43
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/ft           16.4 ms         16.4 ms           43
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/var/hb       17.6 ms         17.6 ms           40
+    BM_Shape/en-words.txt/SourceSerifVariable-Roman.ttf/var/ft       17.8 ms         17.8 ms           39
+
+ src/hb-ot-font.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 12fff976b6cc4433dd3ed6aa7cf852031f7bd289
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 13:42:25 2022 -0600
+
+    [ot-var] Use atomic int for cached-serial
+
+ src/hb-ot-font.cc | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 0919eaa6e84c4de9d9fbaab8938474295a480892
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 13:07:24 2022 -0600
+
+    [ot-font] Remove lock around cache
+    
+    Not needed.
+
+ src/hb-ot-cff1-table.hh |  6 +++---
+ src/hb-ot-font.cc       | 46 +++++++++++++++++++++++++---------------------
+ 2 files changed, 28 insertions(+), 24 deletions(-)
+
+commit 3548b6025fb53fd287910642cd52b12991e82d2d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 19:39:52 2022 -0600
+
+    [ot-font] Cache h-advances for variable fonts
+
+ src/hb-ot-font.cc | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 89 insertions(+), 8 deletions(-)
+
+commit 39a07bf3eba74ab91827cb43b98127ae85f781e2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 19:13:05 2022 -0600
+
+    [ot-font] Rename cache to varStore_cache
+
+ src/hb-ot-font.cc | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+commit 970e03ecaebdc5cf5120ec80cb6716dd9bd40e52
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 19:02:36 2022 -0600
+
+    [ot-font] Add a hb_ot_font_t struct
+
+ src/hb-ot-font.cc | 41 +++++++++++++++++++++++++++++++----------
+ 1 file changed, 31 insertions(+), 10 deletions(-)
+
+commit 80c49933c6dead0ebb6678eece7520e22552e6c8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 19:02:27 2022 -0600
+
+    [hb-ft] Adjust serial signature
+
+ src/hb-ft.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 56e0ff9ea129aa91dfcc746cd61f8cbbc427dba7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 12:30:46 2022 -0600
+
+    [ft] If hb_font changed, update FT_Face
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/2270
+    
+    Rather untested.
+
+ src/hb-ft.cc | 100 +++++++++++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 67 insertions(+), 33 deletions(-)
+
+commit d0de389de8f65f39ae97bb8b359d4b05cabd12b4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 12:18:43 2022 -0600
+
+    [font] Fix test
+
+ src/hb-font.cc | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+commit a2015cd300282b05d7082fbdbdf1c0a93a8993fb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 12:15:00 2022 -0600
+
+    [font] Add a separate serial_coords
+
+ src/hb-font.cc | 11 ++++++-----
+ src/hb-font.h  |  2 +-
+ src/hb-font.hh |  3 ++-
+ 3 files changed, 9 insertions(+), 7 deletions(-)
+
+commit 8629df188ad1a8563c2118de2cde983bdac4ecdd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 12:06:22 2022 -0600
+
+    [ft] Discard advance cache if font changed
+    
+    Uses newly added font serial API.
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/2270
+    
+    But doesn't set new coords on the FT_Face. That's a lot more
+    work :(.
+
+ src/hb-ft.cc | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 48db1c958323cd1739a1e6fe8f6dfd625db7ad5d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 12:03:32 2022 -0600
+
+    [font] Add serial API
+    
+    New API:
+    + hb_font_get_serial()
+    + hb_font_changed()
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/2426
+    
+    Unused internally as of now.
+
+ docs/harfbuzz-sections.txt |  2 ++
+ src/hb-font.cc             | 88 ++++++++++++++++++++++++++++++++++++++++++++++
+ src/hb-font.h              |  6 ++++
+ src/hb-font.hh             |  1 +
+ 4 files changed, 97 insertions(+)
+
+commit 0fe18692286257df06f9af3fe8317edd4e7308dd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 17:49:15 2022 -0600
+
+    [benchmark-set] Pause timing around set copy initialization
+
+ perf/benchmark-set.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit ce5435a862cd6078e86698073186652af8639aa4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 16:34:04 2022 -0600
+
+    [benchmark-set] Remove use of rand() inside benchmark
+
+ perf/benchmark-set.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit efa2a5796ef06fd035bd58a573c249f90a141ead
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 16:24:00 2022 -0600
+
+    [map] Add hb_map_copy()
+    
+    New API:
+    + hb_map_copy()
+
+ docs/harfbuzz-sections.txt |  1 +
+ src/hb-map.cc              | 19 +++++++++++++++++++
+ src/hb-map.h               |  3 +++
+ src/hb-set.cc              |  1 +
+ 4 files changed, 24 insertions(+)
+
+commit 3b28cff9c078d9a29b611a2b7fe014b8e4168762
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 04:42:17 2022 -0600
+
+    [cff1] Fix null dereference on memory alloc failure
+
+ src/hb-ot-cff1-table.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 8df9aba99774c39839d05231c5ee7e38a2614663
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 26 03:59:21 2022 -0600
+
+    Actually try to fix null-size undefined behavior
+    
+    Related to:
+    https://github.com/harfbuzz/harfbuzz/pull/2067
+    https://bugzilla.mozilla.org/show_bug.cgi?id=1577584
+
+ src/hb-null.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit e00c7358a0bb953d028b167911e557acdbaeb485
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 25 22:35:23 2022 +0000
+
+    [subset] special case table presence check for hb_face_create_from_tables faces.
+
+ src/hb-subset.cc | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+commit 6a149a09e73cfb26cd375ad8a5fcc1e2f3b7d75c
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 25 22:22:35 2022 +0000
+
+    [subset] fix use of lazy static constructor.
+
+ src/hb-subset.cc | 119 ++++++++++++++++++++++++++++---------------------------
+ 1 file changed, 60 insertions(+), 59 deletions(-)
+
+commit d4c7939eb7483d818671f60033ff4e09eaea9816
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 25 22:11:32 2022 +0000
+
+    [subset] use a list of known tables instead of handled tables.
+
+ src/hb-subset.cc | 69 +++++++++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 46 insertions(+), 23 deletions(-)
+
+commit 3472f73b79e3bd257507df29958b1c6145fb8bb5
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 25 21:49:12 2022 +0000
+
+    [subset] also include no subset tables when guessing which tables are present.
+
+ src/hb-subset.cc       | 112 ++++++++++++++++++++++++-------------------------
+ test/api/test-subset.c |   1 +
+ 2 files changed, 57 insertions(+), 56 deletions(-)
+
+commit 9564d987390a8437e2d077432629899a7bcd6d0f
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 25 21:16:37 2022 +0000
+
+    [subset] fix subsetting of faces created via hb_face_create_for_tables.
+    
+    Fixes #3609.
+
+ src/hb-subset.cc       | 83 ++++++++++++++++++++++++++++++++++++++++++++++++--
+ test/api/test-subset.c | 36 ++++++++++++++++++++++
+ 2 files changed, 117 insertions(+), 2 deletions(-)
+
+commit 6010feeeb543d5944c4d112571a94ea99807aca9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 24 09:00:44 2022 -0600
+
+    [varStore] Rename variable as per review
+    
+    https://github.com/harfbuzz/harfbuzz/pull/3605
+
+ src/hb-ot-layout-common.hh | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+commit 89939c9cc372344196d5b32ed01c5cfa2a920b83
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Tue May 24 03:29:23 2022 +0200
+
+    [ci] Fix fedora-valgrind job
+    
+    Fedora 33 is EOL since 2021-11-30, try the latest Fedora release (36).
+
+ .circleci/config.yml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 8a7cfe17874b19afbba0378f1ace9bd21aab46ba
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 14:36:06 2022 -0600
+
+    [perf/benchmark-shape] Test ft font backend as well
+
+ perf/benchmark-shape.cc | 64 +++++++++++++++++++++++++++++++++++++++----------
+ perf/meson.build        |  2 +-
+ 2 files changed, 52 insertions(+), 14 deletions(-)
+
+commit d473397831fafa216a1658de830dc0178ec0fab3
+Merge: e1f4445df 3eb7eff48
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 23 12:24:38 2022 -0600
+
+    Merge pull request #3605 from harfbuzz/cache-varstore
+    
+    Cache varstore
+    https://github.com/harfbuzz/harfbuzz/pull/3605
+
+commit 3eb7eff487f5aa3abaf5f7e1e40f96cb1de1c364
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 21 15:25:53 2022 -0600
+
+    Remove varstore cache use if HB_NO_VAR
+
+ src/hb-ot-font.cc            | 12 ++++++++++++
+ src/hb-ot-layout-gsubgpos.hh | 15 +++++++++++++--
+ 2 files changed, 25 insertions(+), 2 deletions(-)
+
+commit 099482a37ad51d3dec5e762ab9f1d7dd3d3bb6a3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 21 15:20:23 2022 -0600
+
+    [ot-font] Cache v_advance varstore as well
+
+ src/hb-ot-font.cc | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+commit d9acc045f1651db5ffb42fe38ff2e2895199be87
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 21 14:16:21 2022 -0600
+
+    [VarStore] Sprinkle cache_t type around
+    
+    It's available so no need to use void*.
+
+ src/hb-ot-layout-common.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit da38312e4201185621b440faf8faf57084a38e7f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 17:32:56 2022 -0600
+
+    [VarStore] Pepper cache with likely()
+
+ src/hb-ot-layout-common.hh | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+commit cf8f00e3548c4e4eb8dca7da598bfe9990b53dd3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 17:14:10 2022 -0600
+
+    [VarStore] Don't use NAN
+    
+    Is faster.
+    
+    With this, I'm seeing 25 to 28% speedup in glyph_h_advances benchmark
+    of benchmark-font for var/hb tests.
+
+ src/hb-ot-layout-common.hh | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+commit 5336ba70f6fc995858881c2e4339e035b16c70a0
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 17:03:18 2022 -0600
+
+    [HVAR] Cache VarStore region scalars
+
+ src/hb-ot-font.cc           |  8 +++++++-
+ src/hb-ot-hmtx-table.hh     |  9 +++++----
+ src/hb-ot-var-hvar-table.hh | 11 ++++++++---
+ 3 files changed, 20 insertions(+), 8 deletions(-)
+
+commit 880f50f7e423858209c490acab13328ca475a89c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 16:50:00 2022 -0600
+
+    Refactor varstore cache
+
+ src/hb-ot-layout-common.hh     | 63 +++++++++++++++++++++++++++---------------
+ src/hb-ot-layout-gpos-table.hh |  6 ++--
+ src/hb-ot-layout-gsubgpos.hh   | 19 +++++--------
+ 3 files changed, 50 insertions(+), 38 deletions(-)
+
+commit f2a2fb91a34cde23448eadd11a0fbc294153fcf5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 16:06:05 2022 -0600
+
+    [GPOS] Cache VarStore region scalars
+
+ src/hb-ot-layout-common.hh     |  2 ++
+ src/hb-ot-layout-gpos-table.hh | 13 +++++++------
+ src/hb-ot-layout-gsubgpos.hh   | 12 +++++++++++-
+ 3 files changed, 20 insertions(+), 7 deletions(-)
+
+commit 5fbc70c59b2d867e2142dba821802e2f26237c81
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 14:07:27 2022 -0600
+
+    [VarStore] Add cache API
+
+ src/hb-ot-layout-common.hh | 67 +++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 49 insertions(+), 18 deletions(-)
+
+commit e1f4445dff20a221287ad7b4c0140d03fe077866
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 21 15:11:53 2022 -0600
+
+    [benchmark-shape] Allow taking multiple tests from cmdline
+
+ perf/benchmark-shape.cc | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+commit 1bf2d5f885ea9c38025970f1587af6ba905acf76
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 21 14:42:50 2022 -0600
+
+    [perf/benchmark-shape] Allow taking text-file/font-file args from cmdline
+
+ perf/benchmark-font.cc  |  5 ++++-
+ perf/benchmark-shape.cc | 32 +++++++++++++++++++++++++++-----
+ 2 files changed, 31 insertions(+), 6 deletions(-)
+
+commit 852a8f04ebbd928333b69ea1f29a268e119910f7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Sat May 21 14:31:09 2022 -0600
+
+    [perf/benchmark-font] Allow benchmarking fonts specified on cmdline
+
+ perf/benchmark-font.cc | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+commit 05e82aa12e8b85d7eaa31f7d25beb6bfd4c147ed
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 12:17:31 2022 -0600
+
+    [ft] Add missing lock to kerning function
+
+ src/hb-ft.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit da4b6f1527002d5e88b5556fb269e8434fd22598
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 17:21:04 2022 -0600
+
+    [benchmark-shape] Add variable fonts
+
+ perf/benchmark-shape.cc | 53 ++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 41 insertions(+), 12 deletions(-)
+
+commit 4ea2725704ae7e4d345b036ca2f574330233f00d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 13:19:27 2022 -0600
+
+    [set/map] Expose hash API publicly
+    
+    New API:
+    + hb_set_hash()
+    + hb_map_hash()
+
+ docs/harfbuzz-sections.txt |  2 ++
+ src/hb-map.cc              | 17 +++++++++++++++++
+ src/hb-map.h               |  3 +++
+ src/hb-set.cc              | 17 +++++++++++++++++
+ src/hb-set.h               |  3 +++
+ 5 files changed, 42 insertions(+)
+
+commit 2e186d9f2412ec858d94f7a8084b2fd7d3483449
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 13:15:52 2022 -0600
+
+    [buffer] Improve hash function of segment_properties_t
+
+ src/hb-buffer.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit aee123fc83388b8f5acfb301d87bd92eccc5b843
+Author: Khaled Hosny <khaled@aliftype.com>
+Date:   Fri May 20 21:07:25 2022 +0200
+
+    4.3.0
+
+ NEWS                   | 17 +++++++++++++++++
+ configure.ac           |  2 +-
+ docs/harfbuzz-docs.xml |  1 +
+ meson.build            |  2 +-
+ src/hb-map.cc          |  2 +-
+ src/hb-version.h       |  6 +++---
+ 6 files changed, 24 insertions(+), 6 deletions(-)
+
+commit 975a5f919467c9bc4cad1340ebf07ae32bf07e14
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 12:34:49 2022 -0600
+
+    [array] Use hb_memcmp instead of memcmp
+    
+    Fixes ubsan error.
+
+ src/hb-array.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 55804e8d68af0685867d20a1796b952c6ff8db60
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 20 11:40:44 2022 -0600
+
+    [hb-ft] Minor rearrange of struct members
+    
+    To make clear what members the lock protects.
+
+ src/hb-ft.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 4e11da054d2c527fa64b33d49b33e3aa6b49077c
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri May 20 01:42:34 2022 +0000
+
+    [repacker] update repacker test golden file.
+    
+    Changed due to removal of Kahn sorting.
+
+ test/api/fonts/repacker_expected.otf | Bin 1400 -> 1400 bytes
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+
+commit cbf8f44c9b6de43387c61fdd43cf6bf0b89c3c08
+Author: Garret Rieger <grieger@google.com>
+Date:   Thu May 19 21:25:21 2022 +0000
+
+    [subset-perf] swap instead of copying vertice's when reordering during sort.
+
+ src/hb-repacker.hh  | 22 ++++++++++++++++------
+ src/hb-serialize.hh | 11 ++++++++++-
+ 2 files changed, 26 insertions(+), 7 deletions(-)
+
+commit b32ca2a292f256a40e445990f104f09c5920d0bd
+Author: Garret Rieger <grieger@google.com>
+Date:   Thu May 19 20:45:39 2022 +0000
+
+    [subset-perf] remove sort_kahn from repacker.
+    
+    Without an optimized FIFO queue implementation it's nearly as slow as the now optimized sort_shortest_distance.
+
+ src/hb-repacker.hh   | 53 ---------------------------------
+ src/test-repacker.cc | 84 ----------------------------------------------------
+ 2 files changed, 137 deletions(-)
+
+commit 4266cf3be266aef27a5d60530860915c68ba03e1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 18:15:46 2022 -0600
+
+    [array] Specialize operator== for bytes_t and ubytes_t
+
+ src/hb-array.hh | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+commit 6eaa22e9d71d2b09d4bd211026194d618dcc8aad
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 18:00:58 2022 -0600
+
+    [serialize] Reduce link_t size from 16 to 12
+
+ src/hb-serialize.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 30ba9a39e2249b86310c36564373f4f0347012e1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 17:34:58 2022 -0600
+
+    [vector] Add emplacing push implementation
+
+ src/hb-vector.hh | 24 +++++++++++++++++++++---
+ 1 file changed, 21 insertions(+), 3 deletions(-)
+
+commit 25393288f0d6b98355bc4b72bce15ab6f77a5b0e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 17:19:21 2022 -0600
+
+    [test] Fix compiler warning
+
+ test/api/test-set.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+commit 73b8360dcfb57eaa9acffc7967015a113421eeda
+Author: Garret Rieger <grieger@google.com>
+Date:   Thu May 19 22:59:51 2022 +0000
+
+    [subset] fix fuzzer found underflow when heap push fails.
+    
+    Fixes https://oss-fuzz.com/testcase-detail/5148625505746944.
+
+ src/hb-priority-queue.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit f1bf14ea89ea082e5edd4e9c90738370bffcab1c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 16:42:35 2022 -0600
+
+    Revert "[set] Cache hash value"
+    
+    This reverts commit 44952bcc259a906b8875ed62dc40de96ade8b95c.
+    
+    While we investivate https://github.com/harfbuzz/harfbuzz/issues/3599
+
+ src/hb-bit-set.hh | 22 +---------------------
+ 1 file changed, 1 insertion(+), 21 deletions(-)
+
+commit b4d1ec310cd2c8a6e250c71f865c45fe7cadd5fa
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 16:06:21 2022 -0600
+
+    [algs] Declare coerce() as constexpr
+
+ src/hb-algs.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 2fdb7616f589ebb9fc060fdb88745e0219a78a14
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 16:00:43 2022 -0600
+
+    [map Further adjust hash function
+
+ src/hb-map.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 01fc90b68c023d380f3cd44e13b21972b3a41dcf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 16:00:06 2022 -0600
+
+    [map] Adjust hash function
+
+ src/hb-map.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit a47b0aebf5f8d56dd78ddd651d40727b729a7577
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:52:00 2022 -0600
+
+    [vector] Fix remove() implementation
+    
+    test-vector under valgrind happy now.
+
+ src/hb-vector.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 3bd755c32dc7c6ba189783daf89e4cde81715483
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:51:18 2022 -0600
+
+    [test-vector] Test remove()
+    
+    Currently buggy. Valgrind confirms.
+
+ src/test-vector.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 58f848daa8f596007a8dadee3fcb462548def980
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:42:54 2022 -0600
+
+    [set/map] Adjust hash function return type
+
+ src/hb-bit-page.hh           | 8 ++++----
+ src/hb-bit-set-invertible.hh | 2 +-
+ src/hb-bit-set.hh            | 6 +++---
+ src/hb-map.hh                | 4 ++--
+ src/hb-set.hh                | 2 +-
+ 5 files changed, 11 insertions(+), 11 deletions(-)
+
+commit 6544fc284f55ec1d3199bc610eeac39af935df9c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:28:09 2022 -0600
+
+    [vector] Add further copy implementation
+
+ src/hb-vector.hh | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+commit c19f1169521c6fa95c690285a3d24123f387a98e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:27:52 2022 -0600
+
+    [meta] Remove non-existing gcc4 trait implementation
+
+ src/hb-meta.hh   | 2 --
+ src/hb-vector.hh | 2 +-
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+commit 679b900e9b27fdecb9a694c58f71e7bc9e2cd125
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:27:32 2022 -0600
+
+    [meta] Fix gcc4 trait implementation
+
+ src/hb-meta.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit fb77f48ffd3fe7fcd17843b9cdc6ca677d36602c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:02:10 2022 -0600
+
+    [vector] Optimize vector copy
+
+ src/hb-meta.hh   |  4 ++++
+ src/hb-vector.hh | 30 ++++++++++++++++++++++++++++--
+ 2 files changed, 32 insertions(+), 2 deletions(-)
+
+commit 28b44ac46a24f6987d2c2565e0ac72d5b2763d81
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:01:56 2022 -0600
+
+    [set] Switch set copy to vector operator =
+    
+    Slows it down currently.
+
+ src/hb-bit-set.hh | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+commit 37d3275dec01edfafe2cc744ed85a3febb964594
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 15:01:23 2022 -0600
+
+    [test-vector] Enable disabled test
+    
+    This seems to work already.
+
+ src/test-vector.cc | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit 544ffb913ea515fae77f26714a1c7c620cdab0ed
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 14:50:12 2022 -0600
+
+    [set] Adjust grow_vector condition
+
+ src/hb-vector.hh | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+commit 0623aa598ba6a7cc14d00091935bc8811b3c6aac
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 14:12:42 2022 -0600
+
+    [benchmark-set] Add benchmark for set copy
+
+ perf/benchmark-set.cc | 23 +++++++++++++++++++++++
+ src/hb-set.hh         |  8 ++++----
+ 2 files changed, 27 insertions(+), 4 deletions(-)
+
+commit 44952bcc259a906b8875ed62dc40de96ade8b95c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 14:02:48 2022 -0600
+
+    [set] Cache hash value
+
+ src/hb-bit-set.hh | 26 +++++++++++++++++++++++---
+ 1 file changed, 23 insertions(+), 3 deletions(-)
+
+commit 844ac328e46f9bfcc5481f2dd525603c9a448ffe
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 13:54:31 2022 -0600
+
+    [set] Fix hb_set_t hash stability
+
+ src/hb-bit-page.hh | 2 +-
+ src/hb-bit-set.hh  | 6 ++++--
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+commit 2d0b1248b23c9eb931c013a35daec62c48ee293f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 13:53:53 2022 -0600
+
+    [test-map] Test hb_set_t hash stability
+    
+    Fails currently.
+
+ src/test-map.cc | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 561e02fefb72be902482fc49dcec66b4c585b798
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 13:38:52 2022 -0600
+
+    [map] Make hb_map_t hashable
+
+ src/hb-map.hh   |  8 ++++++++
+ src/test-map.cc | 21 +++++++++++----------
+ 2 files changed, 19 insertions(+), 10 deletions(-)
+
+commit ad176990895963c1b83274d0ef3c5ae148a4f760
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 13:36:12 2022 -0600
+
+    [map] Add is_equal() / towards making hb_map_t hashable
+    
+    New API:
+    + hb_map_is_equal()
+
+ docs/harfbuzz-sections.txt |  1 +
+ src/hb-map.cc              | 20 ++++++++++++++++++++
+ src/hb-map.h               |  4 ++++
+ src/hb-map.hh              | 15 +++++++++++++++
+ src/test-map.cc            | 24 ++++++++++++++++++++++++
+ 5 files changed, 64 insertions(+)
+
+commit 14a24d8e3f7d9b8379452b1596e4aff6603e1f25
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 13:03:50 2022 -0600
+
+    [vector] Make hb_vector_t hashable
+
+ src/hb-vector.hh |  1 +
+ src/test-map.cc  | 26 ++++++++++++++++++++++++++
+ 2 files changed, 27 insertions(+)
+
+commit 124f9aeb9b4c77fe1e2a733c5aceb9172d169f9f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 19 12:58:02 2022 -0600
+
+    [set] Make hb_set_t hashable
+
+ src/hb-bit-page.hh           |  7 +++++++
+ src/hb-bit-set-invertible.hh |  2 ++
+ src/hb-bit-set.hh            |  6 ++++++
+ src/hb-set.hh                |  4 ++++
+ src/test-map.cc              | 23 +++++++++++++++++++++++
+ 5 files changed, 42 insertions(+)
+
+commit 3ab2c7935f5b9706e4767a6e28ff1dcd739ac271
+Author: Garret Rieger <grieger@google.com>
+Date:   Thu May 19 17:23:36 2022 +0000
+
+    [subset-perf] Signficiantly speed up ClassDef*::subset.
+    
+    Eliminates the usage of a glyph -> klass hash map and replaces it with a vector storing the mapping. This allows us to use the vector directly as the iterator driving the serialize. Approximately 1% speedup for Noto Nastaliq.
+
+ src/hb-ot-layout-common.hh | 72 ++++++++++++++++++++++------------------------
+ 1 file changed, 35 insertions(+), 37 deletions(-)
+
+commit e3e685e5eec1cb400e0b4bd371872cb9394c79bc
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Wed May 18 15:05:55 2022 -0400
+
+    [ot-tags] Fix `min_subtag_len` calculations
+
+ src/gen-tag-table.py   | 12 +++++-------
+ src/hb-ot-tag-table.hh |  3 +--
+ 2 files changed, 6 insertions(+), 9 deletions(-)
+
+commit 0b1c2ff96a333a3e78eeefe54cb9e9509120990a
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 18 23:32:03 2022 +0000
+
+    [subset-perf] Remove extra map lookup in ClassDef subset methods.
+
+ src/hb-ot-layout-common.hh | 15 ++++++++-------
+ 1 file changed, 8 insertions(+), 7 deletions(-)
+
+commit 13ace77f1daaf94d79ad400e3943f71fa5139e70
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 18 22:38:43 2022 +0000
+
+    [subset-perf] Use glyph_map instead of set in ClassDefFormat.
+
+ src/hb-ot-layout-common.hh | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+commit adae2f2272bf51c6b4df2ba5d0e10eb25386e58c
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 18 21:42:28 2022 +0000
+
+    [subset-perf] Cache a glyph map for gsub.
+    
+    This allows us in some cases to avoid using glyph_set_gsub as a filter.
+
+ src/hb-ot-layout-common.hh |  8 +++-----
+ src/hb-subset-plan.cc      | 21 +++++++++++++++++++++
+ src/hb-subset-plan.hh      |  1 +
+ 3 files changed, 25 insertions(+), 5 deletions(-)
+
+commit 202e6c469963fe76f3320a956be8b194adb9089d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 17:12:43 2022 -0600
+
+    [subset] Remove unnecessary test
+
+ src/hb-ot-layout-gsubgpos.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit cedf739646d67e73c06e2569d4be2d7f32e39fd8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 16:52:35 2022 -0600
+
+    Add some commented-out code
+
+ src/hb-ot-layout-common.hh | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+commit 6b62c10f0228d011526ef41772a65e6f12022ddb
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 16:27:54 2022 -0600
+
+    [priority-queue] Remove old init/fini
+
+ src/hb-priority-queue.hh | 7 -------
+ 1 file changed, 7 deletions(-)
+
+commit bff8248a9d44654d7901150e86e684af0cfa8681
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 16:25:03 2022 -0600
+
+    [repacker] Pre-alloc vertices
+
+ src/hb-repacker.hh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 39a424caf04392702b62950c832fd1f67204ba62
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 16:17:16 2022 -0600
+
+    [priority-queue] Optimize heap access
+
+ src/hb-priority-queue.hh   | 18 ++++++++++++------
+ src/test-priority-queue.cc |  8 --------
+ 2 files changed, 12 insertions(+), 14 deletions(-)
+
+commit 9308659fd76bb400da2c869ca2f945760adfaf56
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 16:14:25 2022 -0600
+
+    [priority-queue] Optimize swap()
+
+ src/hb-priority-queue.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit c7317ef7617a1387c88db19582f1b9879a722d7a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 16:03:41 2022 -0600
+
+    [repacker] Avoid destroying and recreating objects
+
+ src/hb-repacker.hh | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit 864e09a0c460916c06d8becbc3480d06692cd634
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 15:59:29 2022 -0600
+
+    [repacker] Reuse allocated vector
+
+ src/hb-repacker.hh | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit ca77f164704c6463b09d973251f6f9995172d8c1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 15:55:49 2022 -0600
+
+    [repacker] Remove unnecessary vector .fini() calls
+
+ src/hb-repacker.hh | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit 4cfc2d668e3df53a6564cef1be65ad0239470123
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 15:32:19 2022 -0600
+
+    [subset] Use a std::move on set_t when feasible
+
+ src/hb-ot-layout-gsubgpos.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 1f578b5a32337011766e078331c0ba8ce4ce8af8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 15:24:40 2022 -0600
+
+    [set] Add pre-allocation internal API
+
+ src/hb-bit-set-invertible.hh | 1 +
+ src/hb-bit-set.hh            | 7 +++++++
+ src/hb-set.hh                | 1 +
+ 3 files changed, 9 insertions(+)
+
+commit 48dfbd54a3c9876e86bcdbeb47ae42300ee9f08f
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 18 21:03:56 2022 +0000
+
+    [subset] minor cleanup.
+
+ src/hb-ot-layout-common.hh | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+commit 482c6e5dc41402e60acf609fca0d9d8e8fbc4d9d
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 18 19:58:55 2022 +0000
+
+    [subset-perf] Speed up Coverage::serialize by caching iterator.
+
+ src/hb-ot-layout-common.hh | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit 14b18725f04bba0dac6da943244230c65d3879d4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 15:14:32 2022 -0600
+
+    In Coverage::iter_t, assume iterators are from same Coverage object
+    
+    No need to support otherwise.
+
+ src/hb-ot-layout-common.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 27141735c3e8caa807c3528ce9793b8c8f05a556
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 15:12:49 2022 -0600
+
+    [subset] Add Coverage::__end__ implementation
+
+ src/hb-ot-layout-common.hh | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+commit c476f58adba6680c655cb7bcbdd28d3bd4b7ad37
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 14:20:23 2022 -0600
+
+    [subset] Write CoverageFormat2::intersects_coverage() as bsearch()
+
+ src/hb-ot-layout-common.hh | 23 ++++++++++++++---------
+ 1 file changed, 14 insertions(+), 9 deletions(-)
+
+commit 63c6695108ceb19b4b79e00782c3106801d7dc01
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 13:53:52 2022 -0600
+
+    [ot-layout] Cosmetic
+    
+    The implementation of HBUINT16 operator == is slower than just
+    comparing to ints.
+
+ src/hb-ot-layout-common.hh | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+commit 777debd748dfd803bbd16bcc1bbf2afd7db2fc82
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 13:46:06 2022 -0600
+
+    [subset] Rewrite CoverageFormat2::intersects as dagger
+
+ src/hb-ot-layout-common.hh | 10 +++-------
+ 1 file changed, 3 insertions(+), 7 deletions(-)
+
+commit cf5001fac7770286082ced9d3c5c5fefa3b19d79
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 13:38:29 2022 -0600
+
+    [subset] Optimize CoverageFormat2::intersected_coverage_glyphs
+
+ src/hb-ot-layout-common.hh | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit 6f37c2079815bc0ac9193c8e9028da4872374402
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 13:25:42 2022 -0600
+
+    [subset] Minor rewrite in CoverageFormat2::serialize()
+
+ src/hb-ot-layout-common.hh | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+commit e91863b7173543b850e1758873f96c76c67f8ce8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 12:39:55 2022 -0600
+
+    [subset-cff] Pre-size map in subr_remap_t::create()
+
+ src/hb-bimap.hh             | 6 ++++++
+ src/hb-subset-cff-common.hh | 1 +
+ 2 files changed, 7 insertions(+)
+
+commit ce60462173c7d22f9bad8531a2490a551f004197
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 12:34:27 2022 -0600
+
+    [subset-plan] Pre-size maps in _create_old_gid_to_new_gid_map()
+
+ src/hb-subset-plan.cc | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+commit f82ee17a75dda53ac5c506136221b93eed53aee1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 12:17:43 2022 -0600
+
+    [map] Pre-size map in constructor if we can
+
+ src/hb-map.hh           | 13 ++++++++-----
+ src/hb-ot-cmap-table.hh |  4 ++--
+ 2 files changed, 10 insertions(+), 7 deletions(-)
+
+commit b5aa8a27eac851503eaee086912f269b51e68724
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:58:58 2022 -0600
+
+    [subset-cff] Cosmetic
+
+ src/hb-subset-cff-common.hh | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+commit 0b201623f54420a898d3538c8673b450923bcac1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:58:22 2022 -0600
+
+    [subset-cff] Fix previous commit
+    
+    Oops!
+
+ src/hb-subset-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 4792309265ea17aea0c5fd6821ed453fe8427ab4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:54:08 2022 -0600
+
+    [subset-cff] Access vector directly
+
+ src/hb-subset-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 7c86f2e763e44b4c96cd26f1ce06225b4aba977f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:45:27 2022 -0600
+
+    [subset-cff] Pre-alloc out buffer
+
+ src/hb-subset-cff-common.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 0761e7cdfd00d5347657bdf009c3035be4ebab44
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:37:57 2022 -0600
+
+    [subset-cff] Avoid resetting buffer as encoder does
+
+ src/hb-subset-cff-common.hh | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit 71aa10a3942081f2c4ce0c2c5d4c3897d13d887c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:37:24 2022 -0600
+
+    [subset-cff] Manually grow vector to avoid memset overhead
+
+ src/hb-subset-cff-common.hh | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+commit f455cc53fd4a30682c549fcad6d4112b98688aca
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:31:55 2022 -0600
+
+    [subset-cff] Reuse buffer allocation
+
+ src/hb-subset-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 3e4ab2ad9c2de17218c16787b59d63c236262d8f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:16:46 2022 -0600
+
+    [perf/benchmark-ot] Add zh-hans
+
+ perf/benchmark-ot.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 6e668a2adefdc186dcd300136b3535c43d6fdffd
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:16:11 2022 -0600
+
+    [perf/benchmark-ot] Rename test
+
+ perf/benchmark-ot.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e24797aeac65aaa1edd836bf9708f488044d3939
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:10:10 2022 -0600
+
+    [ot-tags] Follow-up to previous commit
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+
+ src/gen-tag-table.py   | 16 ++++++++--------
+ src/hb-ot-tag-table.hh | 18 +++++++++---------
+ 2 files changed, 17 insertions(+), 17 deletions(-)
+
+commit f5d619be79e9f23f67f23513e60c546fc498f1b8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 18 11:04:52 2022 -0600
+
+    [ot-tags] Further gate the slow complex case, and add more tests
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Still 'zh-trad' is the slowest case.
+    
+    --------------------------------------------------------------------------------------------------
+    Benchmark                                                        Time             CPU   Iterations
+    --------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_trad          136 ns          136 ns      5107838
+    BM_hb_ot_tags_from_script_and_language/COMMON ab_abcd          115 ns          115 ns      6103104
+    BM_hb_ot_tags_from_script_and_language/COMMON ab_abc          25.4 ns         25.3 ns     27674482
+    BM_hb_ot_tags_from_script_and_language/COMMON abcdef_XY       20.2 ns         20.1 ns     34795719
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY         19.4 ns         19.3 ns     36390401
+    BM_hb_ot_tags_from_script_and_language/COMMON cxy_CN          33.5 ns         33.4 ns     20998939
+    BM_hb_ot_tags_from_script_and_language/COMMON exy_CN          25.1 ns         25.0 ns     27705832
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN           34.2 ns         34.1 ns     20564356
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US           15.5 ns         15.5 ns     45032204
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US            15.9 ns         15.8 ns     44412379
+    BM_hb_ot_tags_from_script_and_language/COMMON none            4.72 ns         4.71 ns    149101665
+    BM_hb_ot_tags_from_script_and_language/LATIN none             4.72 ns         4.70 ns    149254498
+
+ perf/benchmark-ot.cc   | 3 +++
+ src/gen-tag-table.py   | 3 +++
+ src/hb-ot-tag-table.hh | 3 +++
+ 3 files changed, 9 insertions(+)
+
+commit 9c64bda21dd4215a3caa32e4127d9f2017e50de2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 17:31:18 2022 -0600
+
+    [ot-tag] Whitespace
+
+ src/hb-ot-tag.cc | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit 3df8017e9b7ea2b72477294133563b4ff304a007
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 17:29:39 2022 -0600
+
+    [ot-tag] Optimize subtag_matches() more
+
+ src/gen-tag-table.py   |   2 +-
+ src/hb-ot-tag-table.hh | 112 ++++++++++++++++++++++++-------------------------
+ src/hb-ot-tag.cc       |   5 +--
+ 3 files changed, 59 insertions(+), 60 deletions(-)
+
+commit b231fc2dbcee402d1cff578371f9ad89ff594bb2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 17:02:48 2022 -0600
+
+    [perf/benchmark-ot] Add a couple more test cases
+
+ perf/benchmark-ot.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 3524b14fa06dbf9caddef1d2f598e2f4f46315c1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 17:02:48 2022 -0600
+
+    [perf/benchmark-ot] Add a couple more test cases
+
+ perf/benchmark-ot.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 7f6e8c5536fd13a56b4bd030233960aa1af38d05
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 16:58:35 2022 -0600
+
+    [ot-tags] Optimize subtag_matches() further
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Comparing before to after
+    Benchmark                                                               Time             CPU      Time Old      Time New       CPU Old       CPU New
+    ----------------------------------------------------------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY                -0.3371         -0.3371            71            47            71            47
+
+ src/hb-ot-tag.cc | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+commit 27c11405a263ad43d24e2ed460e15f247ac06d17
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 16:51:51 2022 -0600
+
+    [ot-tag] Optimize subtag_matches
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+
+ src/hb-ot-tag.cc | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit a07d818597385c6d83265f8320b9c1334c539758
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 16:46:10 2022 -0600
+
+    [ot-tag] Add a likely() to the cache hit case
+
+ src/hb-ot-tag.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 0ff5d36cd451dffe51a8c0637b4a544882663a1d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 16:34:52 2022 -0600
+
+    [perf/benchmark-ot] Fix benchmark
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Ouch!
+    
+    These are the current numbers:
+    
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       78.0 ns         77.7 ns      8917912
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         44.9 ns         44.8 ns     15475318
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         17.6 ns         17.5 ns     39812340
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          18.2 ns         18.1 ns     38356204
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.76 ns         4.74 ns    148746131
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.73 ns         4.71 ns    148421349
+
+ perf/benchmark-ot.cc | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+commit dfca47f419b6ef5c6df813822e4b10a7cec92434
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 16:21:02 2022 -0600
+
+    [ot-tag] Cache last bsearch result
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Humm. Looks like not all of the fat is bsearch overhead now. I cached
+    the last bsearch result, but most of the time is still there. I'm
+    baffled.
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.08 ns         8.05 ns     84500482
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         42.2 ns         42.1 ns     16722006
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         16.1 ns         16.0 ns     43461527
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          16.5 ns         16.5 ns     42448505
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.34 ns         4.33 ns    161290530
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.34 ns         4.33 ns    162339799
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.13 ns         8.11 ns     80438134
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         40.0 ns         39.9 ns     17487939
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         12.7 ns         12.7 ns     55124394
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          13.1 ns         13.0 ns     53660125
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.61 ns         4.60 ns    151394104
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.70 ns         4.68 ns    150402847
+
+ src/hb-ot-tag.cc | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+commit 909f00ac6e6b3eb459f0553b84fe508bb697e9af
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 15:51:41 2022 -0600
+
+    [ot-tags] Further speed up language bsearch()
+    
+    Using an integer tag to bsearch, instead of string.
+    
+    Part of: https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.11 ns         8.08 ns     87067795
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         53.6 ns         53.5 ns     13042418
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         24.2 ns         24.1 ns     29052731
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          24.4 ns         24.3 ns     28736769
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.43 ns         4.41 ns    160370413
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.35 ns         4.34 ns    160578191
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       7.97 ns         7.95 ns     85208363
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         41.7 ns         41.6 ns     16945817
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         16.1 ns         16.0 ns     43613523
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          16.5 ns         16.4 ns     42568107
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.30 ns         4.29 ns    164055469
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.29 ns         4.27 ns    163793591
+
+ src/gen-tag-table.py   |    2 +-
+ src/hb-ot-tag-table.hh | 3206 ++++++++++++++++++++++++------------------------
+ src/hb-ot-tag.cc       |   29 +-
+ 3 files changed, 1622 insertions(+), 1615 deletions(-)
+
+commit c460cf74ce2a3ebe5d285e03dc122fb60ff70e01
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 15:30:11 2022 -0600
+
+    [ot-tags] Cosmetic
+
+ src/hb-ot-tag.cc | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit 1c8226ed14c1ac7d82ea5482bdf2a7d019dd38a2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 15:28:50 2022 -0600
+
+    Fix compiler warning
+    
+    On Mac compiler:
+    
+    FAILED: src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o
+    c++ -Isrc/libharfbuzz.0.dylib.p -Isrc -I../src -I. -I.. -I/usr/local/opt/freetype/include/freetype2 -I/usr/local/Cellar/graphite2/1.3.14/include -I/usr/local/Cellar/glib/2.72.1/include/glib-2.0 -I/usr/local/Cellar/glib/2.72.1/lib/glib-2.0/include -I/usr/local/opt/gettext/include -I/usr/local/Cellar/pcre/8.45/include -Xclang -fcolor-diagnostics --coverage -pipe -Wall -Winvalid-pch -Wnon-virtual-dtor -std=c++11 -fno-rtti -O2 -g -fno-exceptions -fno-rtti -fno-threadsafe-statics -fvisibility-inlines-hidden -DHAVE_CONFIG_H -Wno-non-virtual-dtor -MD -MQ src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o -MF src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o.d -o src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o -c ../src/hb-ot-tag.cc
+    In file included from ../src/hb-ot-tag.cc:29:
+    In file included from ../src/hb.hh:481:
+    ../src/hb-array.hh:359:14: error: missing default argument on parameter 'ds'
+                  Ts... ds) const
+                        ^
+    ../src/hb-ot-tag.cc:292:58: note: in instantiation of function template specialization 'hb_sorted_array_t<const LangTag>::bfind<const char *, unsigned int>' requested here
+        if (hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_str, &tag_idx,
+                                                             ^
+    1 error generated.
+
+ src/hb-array.hh  | 9 ++++-----
+ src/hb-ot-tag.cc | 4 +---
+ 2 files changed, 5 insertions(+), 8 deletions(-)
+
+commit c1f4b57c064d41a291976e6d126f7bf0f6e66bc9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 15:19:40 2022 -0600
+
+    [ot-tags] Optimize language comparison
+    
+    Now that we know both strings are of equal len of 2 or 3, optimize.
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.50 ns         8.47 ns     81221549
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         79.6 ns         79.3 ns      8785804
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         40.0 ns         39.9 ns     17462768
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          39.2 ns         39.1 ns     17886793
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.31 ns         4.30 ns    162805417
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.32 ns         4.31 ns    162656688
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.27 ns         8.24 ns     81868701
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         56.1 ns         56.0 ns     12353284
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         24.3 ns         24.2 ns     28955030
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          24.5 ns         24.4 ns     28664868
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.35 ns         4.34 ns    161190014
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.36 ns         4.34 ns    161319000
+
+ src/hb-array.hh  | 14 ++++++++------
+ src/hb-ot-tag.cc | 19 ++++++-------------
+ 2 files changed, 14 insertions(+), 19 deletions(-)
+
+commit dde48d78c1e1c11f3c770491a1d618386b3d92f8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 15:07:49 2022 -0600
+
+    Fix compiler warning
+
+ src/hb-ot-tag.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 15be0deda03f68c0260d07bdc67c8952aa6ccfa7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 14:57:08 2022 -0600
+
+    [ot-tags] Optimize lang_matches()
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.67 ns         8.64 ns     80324382
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         91.2 ns         90.9 ns      7674131
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         41.1 ns         41.0 ns     17174093
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          41.3 ns         41.2 ns     17000876
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.56 ns         4.55 ns    153914130
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.53 ns         4.52 ns    153830303
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.24 ns         8.21 ns     84078465
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         77.5 ns         77.2 ns      9059230
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         38.8 ns         38.7 ns     17790692
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          37.6 ns         37.5 ns     18648293
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.50 ns         4.49 ns    155573267
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.49 ns         4.47 ns    156456653
+
+ src/gen-tag-table.py   |   2 +-
+ src/hb-ot-tag-table.hh | 126 ++++++++++++++++++++++++-------------------------
+ src/hb-ot-tag.cc       |  11 +++--
+ 3 files changed, 70 insertions(+), 69 deletions(-)
+
+commit 407a135baf8ccd53cf1bc3502f3216f3dbcf3328
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 14:45:45 2022 -0600
+
+    [perf/benchmark-ot] Add one more test
+
+ perf/benchmark-ot.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit dd3c858f84105021cf1e427399b971bf26dde8b3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 14:28:28 2022 -0600
+
+    [ot-tags] Speed up hb_ot_tags_from_language()
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    "After that, bulk of the time I suppose is spent in binary-searching the
+    language table. I suggest we split the language table in 2-letter and
+    3-letter tags, to speed-up the vast majority of cases that are
+    2-letter."
+    
+    benchmark-ot, before:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN        112 ns          111 ns      6286271
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US       60.6 ns         60.4 ns     11671176
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US        61.3 ns         61.1 ns     11442645
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.75 ns         4.74 ns    146997235
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.65 ns         4.64 ns    150938747
+    
+    After:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN       89.5 ns         89.2 ns      7747649
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US       38.5 ns         38.4 ns     18199432
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US        39.0 ns         38.9 ns     18049238
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.53 ns         4.52 ns    154895110
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.54 ns         4.52 ns    154762105
+
+ src/gen-tag-table.py   |  55 +++----
+ src/hb-ot-tag-table.hh | 409 +++++++++++++++++++++++++------------------------
+ src/hb-ot-tag.cc       |  45 ++++--
+ 3 files changed, 270 insertions(+), 239 deletions(-)
+
+commit 9baccb986087319ae56e77624082036063d67d90
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 13:34:34 2022 -0600
+
+    [ot-tags] Speed up hb_ot_tags_from_complex_language()
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    2. All the subtag_matches outside the switch match long strings (>= 6 or so).
+       As such, check the tag for such length before going into any of them.
+    
+    benchmark-ot, before:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN        172 ns          171 ns      4083155
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US        120 ns          119 ns      5849947
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US         113 ns          112 ns      5840326
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.66 ns         4.64 ns    151396224
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.66 ns         4.64 ns    149019593
+    
+    After:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN        112 ns          112 ns      6357763
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US       60.5 ns         60.3 ns     11475091
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US        54.9 ns         54.8 ns     12575690
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.61 ns         4.59 ns    152388450
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.66 ns         4.64 ns    151497600
+
+ src/gen-tag-table.py   |  41 +++++++++++-----
+ src/hb-ot-tag-table.hh | 126 +++++++++++++++++++++++++------------------------
+ 2 files changed, 95 insertions(+), 72 deletions(-)
+
+commit 26d906b88b324ea953f42acf1b82086cc4ad3642
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 17 13:12:17 2022 -0600
+
+    [perf] Add benchmark-ot
+
+ perf/Makefile.am     |  1 +
+ perf/benchmark-ot.cc | 35 +++++++++++++++++++++++++++++++++++
+ perf/meson.build     | 10 ++++++++++
+ 3 files changed, 46 insertions(+)
+
+commit 629fa8ee87a419c3a2f6b1477d7ecd81571f0d7e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 17:44:50 2022 -0600
+
+    [perf/benchmark-font] Test Roboto as variable even though it's not
+
+ perf/benchmark-font.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 71a0cda869f55c00727fdbbf079b671f7fe374ff
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 17:43:48 2022 -0600
+
+    [perf/benchmark-font] Only certain fonts are variable
+    
+    Don't test every font as variable.
+
+ perf/benchmark-font.cc | 17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+commit fb413f52022aa2edb8a5b64e9d28f826a161d0aa
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 17:08:43 2022 -0600
+
+    [subset/cff] Don't use bitfields for hot bools
+    
+    The struct has room because of alignment, and these bools are hot.
+
+ src/hb-subset-cff-common.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit a4d98b63ea59f17ef5e4795f6048f9cd6baa4340
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 17:02:40 2022 -0600
+
+    [subset/cff1] Collect glyph-to-sid map to avoid an O(n^2) algorithm
+    
+    Saves 13 for largest benchmark:
+    
+    BM_subset/subset_glyphs/SourceHanSans-Regular_subset.otf/10000                    -0.1313         -0.1308            75            65            75            65
+    
+    BM_subset/subset_codepoints/SourceHanSans-Regular_subset.otf/4096                 -0.1009         -0.1004            54            48            54            48
+    BM_subset/subset_codepoints/SourceHanSans-Regular_subset.otf/10000                -0.1067         -0.1066            70            62            69            62
+
+ src/hb-ot-cff1-table.hh | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
+ src/hb-subset-cff1.cc   |  8 +++++++-
+ 2 files changed, 53 insertions(+), 2 deletions(-)
+
+commit b87f48e948077297b823ac929e950d4188ec627d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 16:33:31 2022 -0600
+
+    [cff1] get_sid() move bounds check into each implementation
+
+ src/hb-ot-cff1-table.hh | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+commit e1e359b4daac86cea0a4f02f7f175e93ea9440d7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 15:53:28 2022 -0600
+
+    [cff1] Tighten up range_list_t a bit
+
+ src/hb-subset-cff1.cc | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+commit 3fbac0942da80457f8c226105f5a4a1bdfe502f5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 15:41:11 2022 -0600
+
+    [cff1] Lazy-load & sort glyph names
+    
+    Improves subset benchmarks by up to 70% for small CFF1 subset of
+    non-CID fonts!
+    
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/10                              -0.7067         -0.7071             1             0             1             0
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/64                              -0.4817         -0.4824             1             0             1             0
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/512                             -0.1948         -0.1956             2             2             2             2
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/2000                            -0.0767         -0.0761             6             6             6             6
+
+ src/hb-ot-cff1-table.hh | 74 +++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 53 insertions(+), 21 deletions(-)
+
+commit b58bfd9818243fc1178ecad0731fa24a5aa3f235
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 11:21:45 2022 -0600
+
+    [font] Minor move of code to silence gcc-12 warning
+    
+    See mailing list discussion.
+
+ src/hb-font.cc | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 602e0ca79d1a651fee0cd23d2fa3580621006c87
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 16 10:14:34 2022 -0600
+
+    [cff] Minor restructure of struct
+    
+    Surprisingly this shows tiny benchmark improvement consistently.
+
+ src/hb-cff-interp-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit acdab17ed3507bc9524cb57bef703a983e1031cf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 13 14:14:36 2022 -0600
+
+    [cff] Cosmetic in parsed_values_t
+
+ src/hb-cff-interp-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit b46c7faa9c77e288d16869b9ac609524e0f89468
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 13 14:02:54 2022 -0600
+
+    [cff] Check buf_len, not buf
+    
+    Ouch!
+
+ src/hb-ot-cff1-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 19a8db85458f02f5be268747b85a2c4fab1319b9
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri May 13 18:05:05 2022 +0000
+
+    [subset] fix potential integer overflow in gname_t::cmp.
+
+ src/hb-ot-cff-common.hh | 3 ++-
+ src/hb-ot-cff1-table.hh | 2 +-
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+commit 2d2f66e1a300e14aac16120f2dc193717502129e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 13 13:53:17 2022 -0600
+
+    [cff-common] In INDEX, return empty bytes if length is zero
+    
+    Before it was possible to return non-null arrayZ.
+
+ src/hb-ot-cff-common.hh | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit a2f132f1fc5ed1c8426dea1b1e27aa1eaf8eeb04
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 13 13:49:17 2022 -0600
+
+    [cff] Check glyph-name's length, not arrayZ
+    
+    As the latter can be non-null while still zero-length.
+
+ src/hb-ot-cff1-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit dc09053f1924a486058a8737fda22567e6d95213
+Author: jeremiazhao <jeremiazhao@tencent.com>
+Date:   Fri May 13 17:54:11 2022 +0800
+
+    fix build requirements for fedora/centos in buiding document
+
+ BUILD.md | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit c657c4e1f8e6f23828fefbc441b01f7bee685c79
+Author: Thomas Devoogdt <thomas.devoogdt@barco.com>
+Date:   Tue May 10 10:00:06 2022 +0200
+
+    [meta] fix type traits on gcc 4.9 #3526
+    
+    Signed-off-by: Thomas Devoogdt <thomas.devoogdt@barco.com>
+
+ src/hb-meta.hh      | 11 +++++++++++
+ src/hb-open-type.hh | 11 ++++++-----
+ src/hb-vector.hh    | 17 +++++++++--------
+ 3 files changed, 26 insertions(+), 13 deletions(-)
+
+commit e4e053c8b3a72295c7f414726085aaa01c137c6f
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri May 13 17:00:57 2022 +0000
+
+    [perf] fix typo in perf Makefile.
+
+ perf/Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e61234c5f75e21901a81df08945daddca5cbfde3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 12 13:20:10 2022 -0600
+
+    [vector] Add tests for move constructor/assignment
+
+ src/test-vector.cc | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+commit 7fa580bc4f83f5440b23531f53b546d52af0f69b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 12 13:05:32 2022 -0600
+
+    [map] Fix map copy/move constructors to actually work
+    
+    Ouch!
+
+ src/hb-map.hh   |  5 +++--
+ src/hb-set.hh   |  3 +--
+ src/test-map.cc | 22 ++++++++++++++++++----
+ src/test-set.cc |  4 +++-
+ 4 files changed, 25 insertions(+), 9 deletions(-)
+
+commit a09dd87ca373c1629c05803e3b8611274cb15a6c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 12 12:58:07 2022 -0600
+
+    [set] Fix set copy/move constructors to actually work
+    
+    Ouch!
+
+ src/hb-set.hh   | 16 ++++++++++------
+ src/test-set.cc | 19 ++++++++++++++-----
+ 2 files changed, 24 insertions(+), 11 deletions(-)
+
+commit 76fc27713f52cc338f0325650c2c7798f5cfa2ce
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 12 12:14:07 2022 -0600
+
+    [vector] Remove explicit std::move
+    
+    Was confusing compilers. Let them figure it out themselves.
+    
+    Makes NotoNastaliqu subsetting/1000 benchmark more than twice faster:
+    
+    Benchmark                                                                       Time             CPU      Time Old      Time New       CPU Old       CPU New
+    ------------------------------------------------------------------------------------------------------------------------------------------------------------
+    BM_subset/subset_glyphs/NotoNastaliqUrdu-Regular.ttf/1000                    -0.5064         -0.5065           111            55           110            55
+    BM_subset/subset_codepoints/NotoNastaliqUrdu-Regular.ttf/1000                -0.5494         -0.5493           132            59           131            59
+
+ src/hb-vector.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit c81198b5bc7d5d0990752b36ad2b1fcdec963abf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 12 11:58:37 2022 -0600
+
+    [set] Tweak move operators a bit
+    
+    Should be equivalent.
+
+ src/hb-bit-set-invertible.hh | 6 +++---
+ src/hb-set.hh                | 4 ++--
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+commit 8dc072d20d87d2986cd58797bc1993c372e5d4d6
+Merge: bff78e651 175319cd8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 11 16:45:40 2022 -0600
+
+    Merge pull request #3579 from harfbuzz/subset-retain-buffer
+    
+    Subset retain buffer
+
+commit 175319cd89fbab431616eb83d4d7c569fe4e8800
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 11 13:47:17 2022 -0600
+
+    [gsubgpos] Clean up OT::ClassDefFormat2::intersected_class_glyphs 0 case
+
+ src/hb-ot-layout-common.hh | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+commit 137af3612bcf038103bfc315f445d6574cba8d2c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 11 13:39:30 2022 -0600
+
+    [gsubgpos] Simplify OT::ClassDefFormat2::intersected_class_glyphs()
+
+ src/hb-ot-layout-common.hh | 39 +++++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 20 deletions(-)
+
+commit 3261e05bdbb067cb9411a38a585bb04be1fb2542
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 11 13:16:31 2022 -0600
+
+    [subset] Optimize ClassDef1::intersected_class_glyphs() for class0
+
+ src/hb-ot-layout-common.hh | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+commit c78d8ba60b49013e3ca98a2d7b030dc5d8c569d8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 11 13:05:41 2022 -0600
+
+    [subset] Allocate same size as source table for GSUB/GPOS/name
+
+ src/hb-subset.cc | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+commit 2e7f1ae48feaa2db8248b7ae01e46ef70e461a31
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 11 12:49:16 2022 -0600
+
+    [subset] Use vector.allocated size instead of tracking buf_size
+
+ src/hb-subset.cc | 10 +++++-----
+ src/hb-vector.hh |  3 +--
+ 2 files changed, 6 insertions(+), 7 deletions(-)
+
+commit f08537963b5157cd9e7a02f6e1695ff6bd27cc1b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 11 12:10:03 2022 -0600
+
+    [cff-subset] Pre-alloc vector for operator decoding
+
+ src/hb-cff-interp-common.hh | 5 +++++
+ src/hb-subset-cff-common.hh | 1 +
+ 2 files changed, 6 insertions(+)
+
+commit 7edd54f3ddadc10307577575f47e943b86198e9d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 18:44:14 2022 -0600
+
+    [perf/benchmark-subset] Minor cleanup
+
+ perf/benchmark-subset.cc | 32 +++++++++++++++++++-------------
+ 1 file changed, 19 insertions(+), 13 deletions(-)
+
+commit aeb50b8942b92cda2b1d5bb03d685f97f79faf5d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 18:06:53 2022 -0600
+
+    [subset] Retain buffer across table subset operations
+
+ src/hb-subset.cc | 61 +++++++++++++++++++++++++++++---------------------------
+ 1 file changed, 32 insertions(+), 29 deletions(-)
+
+commit bff78e651555e6376d2a4b49c323cf5e9fe3a25c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 16:33:37 2022 -0600
+
+    [cff] Convert interpretation environment to use constructor
+
+ src/hb-cff-interp-common.hh      | 29 ++++++++---------------------
+ src/hb-cff-interp-cs-common.hh   | 13 +++++--------
+ src/hb-cff-interp-dict-common.hh |  2 ++
+ src/hb-cff1-interp-cs.hh         |  8 +++-----
+ src/hb-cff2-interp-cs.hh         |  9 ++++-----
+ src/hb-ot-cff1-table.cc          | 16 ++++++++--------
+ src/hb-ot-cff1-table.hh          | 19 ++++++++++---------
+ src/hb-ot-cff2-table.cc          |  8 ++++----
+ src/hb-ot-cff2-table.hh          | 24 ++++++++++--------------
+ src/hb-subset-cff-common.hh      |  8 ++++----
+ 10 files changed, 58 insertions(+), 78 deletions(-)
+
+commit de053e2efbcf0166590868c993bfbe7cc3453a06
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 15:38:37 2022 -0600
+
+    [cff] Convert subr_subset_param_t to use constructor
+
+ src/hb-subset-cff-common.hh | 59 ++++++++++++++++++++++++---------------------
+ 1 file changed, 31 insertions(+), 28 deletions(-)
+
+commit 96140db485b61995b0fe9528b6323a5ea928e5a8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 15:34:33 2022 -0600
+
+    [cff] Convert cff2_extents_param_t to use constructor
+
+ src/hb-ot-cff2-table.cc | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+commit 54544f2a57373b2d74bda55d4a48f58a0121c22c
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 15:31:49 2022 -0600
+
+    [cff] Convert cff1_extents_param_t to use constructor
+
+ src/hb-ot-cff1-table.cc | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+commit 377befd0c72071190029112ee04ab0a06fea60b6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 15:29:12 2022 -0600
+
+    [cff] Convert get_seac_param_t to use constructor
+
+ src/hb-ot-cff1-table.cc | 14 ++++----------
+ 1 file changed, 4 insertions(+), 10 deletions(-)
+
+commit 8fd70362fa4c0f411fc67b15b67b69a7c43431e3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 15:15:49 2022 -0600
+
+    [cff] Use hb_ubytes_t() instead of Null(hb_ubytes_t)
+
+ src/hb-cff-interp-cs-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9033c7f99d5ffe80c349a2ed5e4ef68ca4bed434
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 14:58:53 2022 -0600
+
+    [cff-common] Optimize INDEX::operator[]
+    
+    Previous try showed slowdown in benchmarks, suprisingly.
+    
+    Rewrite it keeping the function, hopefully allowing better optimization.
+
+ src/hb-ot-cff-common.hh | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit 3aace2431b9bd503cb706760d831ae313d059107
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 14:54:04 2022 -0600
+
+    Revert "[cff-common] Optimize INDEX::operator[]"
+    
+    This reverts commit 9edb03ac7ac4b4d0814f3fd1f20cc8d2be99e971.
+
+ src/hb-ot-cff-common.hh | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+commit b31ef081db0d91fd6d3e72a59fc97248ab28a904
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 14:52:40 2022 -0600
+
+    Revert "[cff] Add an unlikely()"
+    
+    This reverts commit 9ba9adb7ed6d48504e97a2af117b7da1fdb28450.
+    
+    This shows slowdown in benchmarks.
+
+ src/hb-cff-interp-cs-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9ba9adb7ed6d48504e97a2af117b7da1fdb28450
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 14:42:50 2022 -0600
+
+    [cff] Add an unlikely()
+
+ src/hb-cff-interp-cs-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9edb03ac7ac4b4d0814f3fd1f20cc8d2be99e971
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 14:25:08 2022 -0600
+
+    [cff-common] Optimize INDEX::operator[]
+
+ src/hb-ot-cff-common.hh | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+commit 52d59bf150b2a6312fe4c3b6f2ec882febe814d9
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue May 10 19:40:37 2022 +0000
+
+    [perf] Make subset benchmark data driven.
+
+ perf/benchmark-subset.cc                           | 153 +++++++--------------
+ .../data}/fonts/NotoSansDevanagari-Regular.ttf     | Bin
+ 2 files changed, 52 insertions(+), 101 deletions(-)
+
+commit 0a42410dc8a8457f49b94a0b533f0b83191ce8d5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Tue May 10 12:05:19 2022 -0600
+
+    [cff2] Change extents/shape stack to be just a number
+    
+    Do the blending immediately.
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3559
+    
+    Benchmark on AdobeVFPrototype shows 35% speedup. Now we're faster
+    than FreeType:
+    
+    Benchmark                                                           Time             CPU      Time Old      Time New       CPU Old       CPU New
+    ------------------------------------------------------------------------------------------------------------------------------------------------
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/hb                    -0.3792         -0.3792          1584           983          1581           982
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/ft                    +0.0228         +0.0224          1220          1248          1218          1245
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/var/hb                -0.3513         -0.3518          1616          1048          1613          1046
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/var/ft                +0.0172         +0.0169          1232          1254          1230          1251
+
+ src/hb-cff-interp-common.hh |  4 +--
+ src/hb-cff2-interp-cs.hh    | 82 +++++++++++++++++++++++++++------------------
+ src/hb-ot-cff2-table.cc     | 24 ++++++-------
+ src/hb-subset-cff2.cc       | 28 ++++++++--------
+ 4 files changed, 77 insertions(+), 61 deletions(-)
+
+commit 5277a5772b0b9ebbbcdec0eae7f1b13d41a8d170
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue May 10 18:14:25 2022 +0000
+
+    [perf] Add benchmarks for CFF subsetting.
+
+ perf/benchmark-subset.cc | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+commit 8f9f0c494b9ea516903e8142e8aba391ddcb581c
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue May 10 17:47:08 2022 +0000
+
+    [subset] Enforce cmap12 group ordering constraints in collect_mapping.
+    
+    Fixes fuzzer issue: https://oss-fuzz.com/testcase-detail/6365271012540416
+
+ src/hb-ot-cmap-table.hh                                |   8 ++++++++
+ ...estcase-minimized-hb-subset-fuzzer-6365271012540416 | Bin 0 -> 161424 bytes
+ 2 files changed, 8 insertions(+)
+
+commit c99ad0f015d1328cbb9803777f66ca491b2cb115
+Merge: c941ece60 1b14d2ff1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 18:52:19 2022 -0600
+
+    Merge pull request #3572 from harfbuzz/cff-stack
+    
+    Cff stack
+
+commit 1b14d2ff136a9f7522995393fda6f6644377657f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 18:15:31 2022 -0600
+
+    [cff] Fix arg-stack peek() impl
+
+ src/hb-cff-interp-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 6106ef8c0f61453c38c58f71a045481bf5546f2d
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 18:12:09 2022 -0600
+
+    [cff] Tighten up arg-stack access
+
+ src/hb-cff-interp-common.hh | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+commit 8c616a6efe7370e110d6a2f822bb1a38bf768ea6
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 17:49:54 2022 -0600
+
+    [cff] Allocate stack inline instead of using hb_vector_t
+    
+    Speeds up glyph_extents and glyph_shape benchmarks for CFF by 10
+    to 16 percent!
+
+ src/hb-cff-interp-common.hh | 18 +++++++-----------
+ 1 file changed, 7 insertions(+), 11 deletions(-)
+
+commit c941ece60fe791b58697a0ac9d92cd27682f0698
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 16:20:22 2022 -0600
+
+    [cff] Use using instead of typedef
+
+ src/hb-cff-interp-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 64d63cebe2968bc8d9882991b5402c7d626ecf90
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 16:16:07 2022 -0600
+
+    [cff-common] Use existing types for str_buff_vec_t
+
+ src/hb-ot-cff-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit e1838ec1f863758bdd3fa33dce8bf8bfb7fa1518
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 16:14:13 2022 -0600
+
+    [cff-common] Remove unused method
+
+ src/hb-ot-cff-common.hh | 11 +----------
+ 1 file changed, 1 insertion(+), 10 deletions(-)
+
+commit 8aa54aaca250e2934bd2c97047db8b40bf027908
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 16:09:56 2022 -0600
+
+    [cff] Replace byte_str_t with hb_bytes_t use
+
+ src/hb-cff-interp-common.hh    | 25 ++++++++++---------------
+ src/hb-cff-interp-cs-common.hh |  6 +++---
+ src/hb-cff1-interp-cs.hh       |  2 +-
+ src/hb-cff2-interp-cs.hh       |  2 +-
+ src/hb-ot-cff-common.hh        | 14 +++++++-------
+ src/hb-ot-cff1-table.cc        |  6 +++---
+ src/hb-ot-cff1-table.hh        | 12 ++++++------
+ src/hb-ot-cff2-table.cc        |  4 ++--
+ src/hb-ot-cff2-table.hh        |  8 ++++----
+ src/hb-subset-cff-common.hh    |  6 +++---
+ src/hb-subset-cff1.cc          |  2 +-
+ 11 files changed, 41 insertions(+), 46 deletions(-)
+
+commit fe1d85a55a53797f0808d1f473475b7ce15eeb92
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 16:04:52 2022 -0600
+
+    [cff] Remove custom byte_str_t impl
+
+ src/hb-cff-interp-common.hh | 35 ++++++++++-------------------------
+ src/hb-ot-cff1-table.hh     |  4 ++--
+ src/hb-ot-cff2-table.hh     |  4 ++--
+ 3 files changed, 14 insertions(+), 29 deletions(-)
+
+commit c8a5f1e3c0cb8b2c0c546e89134cb66b9af2b53a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:49:47 2022 -0600
+
+    [cff-common] Indent
+
+ src/hb-ot-cff-common.hh | 49 +++++++++++++++++++++++++------------------------
+ 1 file changed, 25 insertions(+), 24 deletions(-)
+
+commit be7b2905cb118a5d4d08f42e870fe5f5f5ee9b0e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:48:18 2022 -0600
+
+    [cff-common] Remove unused INDEX::serialize() method
+
+ src/hb-ot-cff-common.hh | 10 ----------
+ 1 file changed, 10 deletions(-)
+
+commit 60390169b65632406391f3492efdbd66c688555f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:44:09 2022 -0600
+
+    [cff-common] Write str_buf_t::total_size() as dagger
+
+ src/hb-ot-cff-common.hh | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+commit 258afb45b7fefa42e36f74731d56862b9367f91e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:40:55 2022 -0600
+
+    [cff-common] Use range-based loop in str_buff_vec_t
+
+ src/hb-ot-cff-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 8bb1a3ce9ae8a24c168a51c6faf16779561138ae
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:38:40 2022 -0600
+
+    [cff-common] Write INDEX offset-size calc using hb_bit_storage()
+
+ src/hb-ot-cff-common.hh | 15 +--------------
+ 1 file changed, 1 insertion(+), 14 deletions(-)
+
+commit 2ccfe84eff7f72159c87012d7e10e9c8ecdbc956
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:35:04 2022 -0600
+
+    [cff-common] Add assert to INDEX::set_offset_at()
+
+ src/hb-ot-cff-common.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 4bcab9e99a7fb7456f5788e2da6fae8fc5b14584
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:30:42 2022 -0600
+
+    [cff-common] Use byte_str_t() instead of Null(byte_str_t)
+
+ src/hb-ot-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 94f7a263228a120754ca31600cabb15de0652501
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:29:14 2022 -0600
+
+    [cff-common] Fix get_size() for Null object
+    
+    The special-casing didn't make sense.
+
+ src/hb-ot-cff-common.hh | 1 -
+ 1 file changed, 1 deletion(-)
+
+commit c9cc7d5d21dc2550e820de841f5d24f5c94dcc7e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:27:27 2022 -0600
+
+    [cff-common] Inline once-used method in INDEX
+
+ src/hb-ot-cff-common.hh | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+commit 11482a3a3927eff8e408f825082f61a202c9be9b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:25:21 2022 -0600
+
+    [cff-common] Remove unused method from INDEX
+
+ src/hb-ot-cff-common.hh | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit d1bb3b08f65965bfc07b11becc3e344554c398cc
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:23:59 2022 -0600
+
+    [cff-common] Hide more INDEX internals
+
+ src/hb-ot-cff-common.hh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit d3b21387fde2923a624903915a58c9745d2602af
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:22:55 2022 -0600
+
+    [cff-common] Remove redundant operator implementation
+
+ src/hb-ot-cff-common.hh | 7 -------
+ 1 file changed, 7 deletions(-)
+
+commit a96b408d805c53c051764b66a7e19aa902c82546
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 9 15:20:16 2022 -0600
+
+    [cff-common] Hide INDEX internals
+
+ src/hb-ot-cff-common.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 335b1d83cf61d1d712e9343a2217594f37018880
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 13:37:11 2022 -0600
+
+    [cff-common] No need to check max-offset in INDEX
+    
+    The length_at() function makes sure out-of-range offsets
+    are discarded. We just need to check the last offset.
+
+ src/hb-ot-cff-common.hh | 19 ++++---------------
+ 1 file changed, 4 insertions(+), 15 deletions(-)
+
+commit b051f3fa8388d25c7023a7f48dfea415bde1c94c
+Author: Garret Rieger <grieger@google.com>
+Date:   Thu May 5 23:27:34 2022 +0000
+
+    [subset] Fix cpal subsetting when there are partial palette overlaps.
+    
+    The existing code doesn't correctly handle the case where palettes partially overlap in the color record array. This changes the subsetting to only share entries in the color record array when palettes have the same first color index. Partially overlapping palettes will be converted to disjoint segments in the color record array.
+    
+    Updates one of the color tests to use multiple palettes.
+    
+    Also fixes fuzzer: https://oss-fuzz.com/testcase-detail/5568200165687296.
+
+ src/hb-ot-color-cpal-table.hh                      |  60 +++++++++++++--------
+ ...ase-minimized-hb-subset-fuzzer-5568200165687296 | Bin 0 -> 220551 bytes
+ .../colr_with_components/colr-table.default.6B.ttf | Bin 4260 -> 4320 bytes
+ .../colr-table.drop-hints-retain-gids.6B.ttf       | Bin 4984 -> 5044 bytes
+ .../colr-table.drop-hints.6B.ttf                   | Bin 4260 -> 4320 bytes
+ .../colr-table.retain-gids.6B.ttf                  | Bin 4984 -> 5044 bytes
+ test/subset/data/fonts/colr-table.ttf              | Bin 26952 -> 27328 bytes
+ 7 files changed, 37 insertions(+), 23 deletions(-)
+
+commit 2884eb97bf448448c8c06f51e1a60acbff33bcbf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 12:54:02 2022 -0600
+
+    [cff-common] Remove special-casing of count=0 in INDEX serialize
+    
+    The generic code-path now can handle count=0.
+
+ src/hb-ot-cff-common.hh | 15 +++------------
+ 1 file changed, 3 insertions(+), 12 deletions(-)
+
+commit fc7f51aecea6b7a66772d4f759f52447f34197f1
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 12:53:19 2022 -0600
+
+    [cff-common] Reduce iterator calls
+
+ src/hb-ot-cff-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit c857b8e3c642476aedea634c294ee101d6ce39f3
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 12:50:37 2022 -0600
+
+    [cff-common] Set INDEX min_size to 2
+    
+    That is what it is, for an empty INDEX.
+
+ src/hb-ot-cff-common.hh | 21 ++++++++++++---------
+ 1 file changed, 12 insertions(+), 9 deletions(-)
+
+commit dd71d2c1c30ca85ddd7b1d7e3a9e2bbdacd6ae7a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 13:02:26 2022 -0600
+
+    [gvar] Protect against offset underflow
+
+ src/hb-ot-var-gvar-table.hh | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 9a6dabd61a1af848abbab21b0152e58875604a37
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 12:01:37 2022 -0600
+
+    [gvar] Remove sanitize check for data array
+    
+    We are not checking in sanitize that offset array is ascending,
+    so this check was bogus.
+
+ src/hb-ot-var-gvar-table.hh | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+commit 38478d1061d4971c6f10910db1b8988aab900bcf
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 12:00:01 2022 -0600
+
+    [gvar] DEFINE_SIZE_ARRAY instead of DEFINE_SIZE_MIN
+
+ src/hb-ot-var-gvar-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 90d278c92e2ef076d2b239fed56a9dc11f4b6c12
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 11:58:53 2022 -0600
+
+    [gvar] Remove requirement that num_glyphs matches the font's
+
+ src/hb-ot-var-gvar-table.hh | 1 -
+ 1 file changed, 1 deletion(-)
+
+commit ca8a0f3ea32af8fdaf2f99ad87a43e82be854f62
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri May 6 11:54:38 2022 -0600
+
+    [gvar] Protect against out-of-range access
+    
+    Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47281
+    Fixes https://oss-fuzz.com/testcase-detail/5508865908670464
+
+ src/hb-ot-var-gvar-table.hh                             |   5 ++++-
+ ...usterfuzz-testcase-hb-subset-fuzzer-5508865908670464 | Bin 0 -> 17004 bytes
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
+commit f10ddb8dd870fd691c8876c1c7151e607aab0625
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 5 11:21:24 2022 -0600
+
+    [cmap] Use -1 as Unicode sentinel, not U+FFFF in Format12 serialize
+
+ src/hb-ot-cmap-table.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 8a19968c8b8f8118e6247489a65edfb707bc838e
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 5 11:17:23 2022 -0600
+
+    [cmap] Use iterator bool operator
+
+ src/hb-ot-cmap-table.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 8bfeea482838a0c4f678c7f666f4520f4f2e8dd9
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 5 10:48:24 2022 -0600
+
+    [subset] Compute set max using previous()
+
+ src/hb-subset-plan.cc | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+commit 00cb8c629d8f5615d316ac6541d6652dfa2d3145
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 5 10:33:50 2022 -0600
+
+    [subset] Don't go into glyf table if it's empty
+
+ src/hb-ot-glyf-table.hh |  2 ++
+ src/hb-subset-plan.cc   | 17 ++++++++++-------
+ 2 files changed, 12 insertions(+), 7 deletions(-)
+
+commit 4fe69bc41327596af540a2f683062b41a4f37f45
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu May 5 10:19:16 2022 -0600
+
+    [subset] Use del_range in _remove_invalid_gids
+
+ src/hb-subset-plan.cc | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+commit 2a42edccbe55ede9ed7bbf643b7bec41698078ed
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 4 17:06:18 2022 -0600
+
+    [subset] Cosmetic; use set bulk array population instead of for loop
+
+ src/hb-subset-plan.cc | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+commit bc5129d7fa6fae7ce4c653b699944dd9416eca68
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 4 22:16:03 2022 +0000
+
+    [perf] use option_t in subset benchmark to select between glyphs and codepoint subset.
+
+ perf/benchmark-subset.cc | 134 ++++++++++++++++++++++-------------------------
+ 1 file changed, 62 insertions(+), 72 deletions(-)
+
+commit 43938ecdc2b5cda45f9499f8c3360a0a3ac0842b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 4 16:59:28 2022 -0600
+
+    [subset] Remove outdated comment
+    
+    I tried something like that. It was slower because of the allocations.
+
+ src/hb-subset-plan.cc | 3 ---
+ 1 file changed, 3 deletions(-)
+
+commit 6212856ce80d1cbdb5ebbd6d8f899e2b1e45d611
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 4 22:16:03 2022 +0000
+
+    [perf] benchmark subsetting via glyphs.
+
+ perf/benchmark-subset.cc | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 78 insertions(+)
+
+commit 6829dd30ad9058170674760f3795fcafe3ed6f27
+Merge: 052812b6b 50db78ba8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 4 16:49:45 2022 -0600
+
+    Merge pull request #3562 from harfbuzz/subset-cmap-no-qsort
+    
+    [subset] In cmap planning, remove a qsort()
+
+commit 50db78ba834b35b96a808c07e550a50b3e1fa5ec
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 4 15:48:18 2022 -0600
+
+    [subset] In cmap planning, remove a qsort()
+
+ src/hb-subset-plan.cc | 30 ++++++++++--------------------
+ 1 file changed, 10 insertions(+), 20 deletions(-)
+
+commit 052812b6ba424b4be677d60a722375f69decb89f
+Merge: f67e6bf79 7cb36e422
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed May 4 15:38:30 2022 -0600
+
+    Merge pull request #3561 from googlefonts/cmap_opt
+    
+    [subset] Further cmap subsetting speed optimizations
+
+commit 7cb36e422218305329102849c156ab94db91cbef
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 4 21:22:26 2022 +0000
+
+    [subset] Re-introduce size threshold in choosing unicode collection method.
+    
+    Threshold is needed since the unicodes set might be an inverted set.
+
+ src/hb-subset-plan.cc | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+commit 42c54eba839f510c885fe1a63732b0f706af1bff
+Author: Garret Rieger <grieger@google.com>
+Date:   Wed May 4 20:21:43 2022 +0000
+
+    [subset] Presize unicode to gid list to unicodes + glyphs size.
+
+ src/hb-subset-plan.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 7c7c01d28cee221f1c64684539c8e6160f144f61
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue May 3 22:40:56 2022 +0000
+
+    [subset] Remove switch to alternate unicode collection at large subset sizes.
+    
+    Benchmarks show that the first path is always faster even at large subset sizes:
+    
+    BM_subset_codepoints/subset_roboto/10_median                              +0.0324         +0.0325             0             0             0             0
+    BM_subset_codepoints/subset_roboto/64_median                              +0.0253         +0.0255             0             1             0             1
+    BM_subset_codepoints/subset_roboto/512_median                             +0.0126         +0.0128             1             1             1             1
+    BM_subset_codepoints/subset_roboto/4000_median                            +0.0500         +0.0491             6             7             6             7
+    BM_subset_codepoints/subset_amiri/10_median                               +0.0338         +0.0332             1             1             1             1
+    BM_subset_codepoints/subset_amiri/64_median                               +0.0238         +0.0234             1             1             1             1
+    BM_subset_codepoints/subset_amiri/512_median                              +0.0066         +0.0063             8             8             8             8
+    BM_subset_codepoints/subset_amiri/4000_median                             -0.0011         -0.0012            13            13            13            13
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/10_median                  +0.0226         +0.0226             0             0             0             0
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/64_median                  +0.0047         +0.0044            20            20            20            20
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/512_median                 +0.0022         +0.0021           165           166           165           166
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/1000_median                -0.0021         -0.0023           166           166           166           165
+    BM_subset_codepoints/subset_noto_devangari/10_median                      +0.0054         +0.0054             0             0             0             0
+    BM_subset_codepoints/subset_noto_devangari/64_median                      +0.0024         +0.0019             0             0             0             0
+    BM_subset_codepoints/subset_noto_devangari/512_median                     +0.0089         +0.0090             5             5             5             5
+    BM_subset_codepoints/subset_noto_devangari/1000_median                    -0.0028         -0.0019             5             5             5             5
+    BM_subset_codepoints/subset_mplus1p/10_median                             +0.0001         +0.0002             0             0             0             0
+    BM_subset_codepoints/subset_mplus1p/64_median                             +0.0073         +0.0075             1             1             1             1
+    BM_subset_codepoints/subset_mplus1p/512_median                            +0.0034         +0.0034             1             1             1             1
+    BM_subset_codepoints/subset_mplus1p/4096_median                           -0.1248         -0.1248             7             6             7             6
+    BM_subset_codepoints/subset_mplus1p/10000_median                          -0.0885         -0.0885            13            12            13            12
+    BM_subset_codepoints/subset_notocjk/10_median                             +0.0031         +0.0032             2             2             2             2
+    BM_subset_codepoints/subset_notocjk/64_median                             -0.0010         -0.0010             2             2             2             2
+    BM_subset_codepoints/subset_notocjk/512_median                            -0.0023         -0.0023             9             9             9             9
+    BM_subset_codepoints/subset_notocjk/4096_median                           -0.1725         -0.1726            28            23            28            23
+    BM_subset_codepoints/subset_notocjk/32768_median                          -0.0277         -0.0287           140           137           140           136
+    BM_subset_codepoints/subset_notocjk/100000_median                         -0.0929         -0.0926           162           147           162           147
+
+ src/hb-subset-plan.cc | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit f0c04114bc229b3b519ed2242689959ccec64098
+Author: Garret Rieger <grieger@google.com>
+Date:   Tue May 3 22:02:59 2022 +0000
+
+    [subset] Embed unicode to gid list vector in subset plan.
+
+ src/hb-ot-cmap-table.hh |  2 +-
+ src/hb-subset-plan.cc   | 35 ++++++++++++++++-------------------
+ src/hb-subset-plan.hh   |  2 +-
+ 3 files changed, 18 insertions(+), 21 deletions(-)
+
+commit f67e6bf79cd1ac3892a2d6dfe6e479483290bd41
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:59:48 2022 -0600
+
+    [perf/benchmark-font] Add benchmark for glyph_h_advance
+
+ perf/benchmark-font.cc | 26 +++++++++++++++++++++++---
+ 1 file changed, 23 insertions(+), 3 deletions(-)
+
+commit 1c0a3d4d16b3ff6864c701fc94aa6878ea82a5c4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:50:54 2022 -0600
+
+    [perf/benchmark-font] Add a couple Noto fonts
+
+ perf/benchmark-font.cc | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 15fa8afb217582bce4d360c43ad7674861dc1278
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:46:41 2022 -0600
+
+    Add fast-path for big-endian 32-bit byteswap
+    
+    Speeds up cmap format-12 decoding by some 40% as measured by
+    the newly added test in perf/benchmark-font!
+
+ src/hb-algs.hh | 24 ++++++++++++++++++++----
+ 1 file changed, 20 insertions(+), 4 deletions(-)
+
+commit 3fff2e9182fc6c3cd8ade0336fa67e71967e82c5
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:31:59 2022 -0600
+
+    [perf/benchmark-font] Cosmetic
+
+ perf/benchmark-font.cc  | 2 +-
+ src/hb-ot-cmap-table.hh | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit 307d2d8bb6e74ad974207d3b9f706568a6a87e75
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:30:22 2022 -0600
+
+    [cmap] Sprinkle some 'unlikely's
+
+ src/hb-ot-cmap-table.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit 85ec5cbcefeb2361536031f2e05518c2d817d98a
+Author: Garret Rieger <grieger@google.com>
+Date:   Mon May 2 22:29:43 2022 +0000
+
+    [subset] In _populate_unicodes_to_retain populate unicodes in order.
+    
+    Allows the set insert to take advantage of page lookup cache.
+
+ src/hb-subset-plan.cc | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+commit 0d1f8dcaf3a45dc8ed61dde370df0874af008870
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:18:53 2022 -0600
+
+    [perf/benchmark-font] Actually make nominal_glyph bench work
+
+ perf/benchmark-font.cc | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+commit 6cf69d10e710cfa7282509c2a43e12618d4673bc
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:07:32 2022 -0600
+
+    [perf/benchmark-font] Add back testing of is_variable
+
+ perf/benchmark-font.cc | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+commit 3aa2ff7988583a7c078032e762cd2bde006fc896
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 16:01:22 2022 -0600
+
+    [perf/benchmark-font] Fix build without freetype
+
+ perf/benchmark-font.cc | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+commit 58a0988b576f915a21f4171f71d6d2603d6f3414
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 15:57:19 2022 -0600
+
+    [perf/benchmark-font] Benchmark get_nominal_glyph
+
+ perf/benchmark-font.cc | 37 ++++++++++++++++++++++++++++++-------
+ 1 file changed, 30 insertions(+), 7 deletions(-)
+
+commit 03f16fab585e57f184642398172bb2e17aa57635
+Merge: a4522df37 6d29903e8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 15:44:41 2022 -0600
+
+    Merge pull request #3560 from harfbuzz/perf-cleanup
+    
+    Perf cleanup
+
+commit 088133d939d8bc4ce3d97eed7d835c1831e68766
+Author: Garret Rieger <grieger@google.com>
+Date:   Mon May 2 21:29:16 2022 +0000
+
+    [subset] cache cp to new gid list in subset plan.
+    
+    This avoids having to recompute the ordered list multiple times during cmap generation.
+
+ src/hb-ot-cmap-table.hh |  9 +--------
+ src/hb-subset-plan.cc   | 30 ++++++++++++++++++++++++++++++
+ src/hb-subset-plan.hh   |  1 +
+ 3 files changed, 32 insertions(+), 8 deletions(-)
+
+commit 6d29903e86d1f6b0fe7ca884a071d047f0ee130b
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 14:03:15 2022 -0600
+
+    [perf/benchmark-font] Parametrize test
+
+ perf/benchmark-font.cc  | 115 +++++++++++++++++++++++++-----------------------
+ perf/benchmark-shape.cc |   2 +
+ 2 files changed, 63 insertions(+), 54 deletions(-)
+
+commit 636c90e81c2eb9a907a1c14d0f3450902d95f65a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 13:41:49 2022 -0600
+
+    [perf/perf] Rename to benchmark-font
+
+ perf/Makefile.am                         |  3 +-
+ perf/{perf-draw.hh => benchmark-font.cc} |  0
+ perf/meson.build                         |  7 ++--
+ perf/perf-extents.hh                     | 65 --------------------------------
+ perf/perf.cc                             |  3 --
+ 5 files changed, 4 insertions(+), 74 deletions(-)
+
+commit 036d03d2e91fc20133150696c405d3281326a552
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 13:39:54 2022 -0600
+
+    [perf/perf] Move all logic to perf-draw, for now
+    
+    To be renamed.
+
+ perf/Makefile.am  |   1 -
+ perf/perf-draw.hh | 124 +++++++++++++++++++++++++++++++++++++++++-------------
+ perf/perf.cc      |  10 -----
+ 3 files changed, 94 insertions(+), 41 deletions(-)
+
+commit 746c3c03c5017b4e1404c65a04a5a6122a6cd831
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 13:26:41 2022 -0600
+
+    [perf/perf] Remove ttf-parser backend
+
+ perf/meson.build     | 11 +------
+ perf/perf-draw.hh    | 91 +++++++++++-----------------------------------------
+ perf/perf-extents.hh | 50 ++++-------------------------
+ perf/perf.cc         |  2 +-
+ 4 files changed, 28 insertions(+), 126 deletions(-)
+
+commit 4aaa0af7d99f7a44a02542ab8a8d467e3f6a3f64
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Mon May 2 13:06:27 2022 -0600
+
+    [perf/perf] Rely on hb-draw to measure ft performance
+
+ perf/perf-draw.hh | 50 +++++++-------------------------------------------
+ 1 file changed, 7 insertions(+), 43 deletions(-)
+
+commit a4522df378259653f6cdda535980c4acee4d3021
+Merge: 4de5352a3 6922a2561
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 18:34:00 2022 -0600
+
+    Merge pull request #3558 from harfbuzz/set-optimize
+    
+    [perf] hb_set_t optimizations and perf suite improvements
+
+commit 6922a2561f75468c328fa158fef289a0b4156d87
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Apr 29 23:30:32 2022 +0000
+
+    [subset] Change serialize_rangeoffset_glyid back to using iterator.
+
+ src/hb-ot-cmap-table.hh | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+commit c66fd50c269a7ab8ab22c404354c783ab5419bcc
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Apr 29 23:18:53 2022 +0000
+
+    [subset] in cmap4 serialization save cp to gid iter to memory.
+    
+    Iterator accesses are slow and it's iterated multiple times.
+
+ src/hb-ot-cmap-table.hh | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+commit 17b98563dc426674d633b79194ce591c8dd38e01
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Apr 29 22:49:02 2022 +0000
+
+    [subset] In cmap4 serialization reduce unnessecary calls into the iterator.
+    
+    Gives ~20% speedup for large subsets.
+
+ src/hb-ot-cmap-table.hh | 30 +++++++++++++++++-------------
+ 1 file changed, 17 insertions(+), 13 deletions(-)
+
+commit 5e241094bfa72840a4142c33264d128b60f12330
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Apr 29 22:44:43 2022 +0000
+
+    [subset] In unicodes cache cleanup if set insert fails.
+
+ src/hb-ot-cmap-table.hh | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+commit 217d38dfc7b7b1152b74ceb46472bf6a05d35f1a
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 16:18:17 2022 -0600
+
+    Try to fix distcheck
+
+ Makefile.am      | 15 +--------------
+ configure.ac     |  1 +
+ perf/Makefile.am | 24 ++++++++++++++++++++++++
+ 3 files changed, 26 insertions(+), 14 deletions(-)
+
+commit a424a92ce5e47b35d3128be1a612d3130c4c85b0
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Apr 29 22:14:03 2022 +0000
+
+    [subset] s/void */intptr_t.
+
+ src/hb-ot-cmap-table.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit aad67f5629b1407df1b3152dfce0aefafbfb4132
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Apr 29 22:01:06 2022 +0000
+
+    [subset] cache results of collect_unicodes.
+
+ src/hb-ot-cmap-table.hh | 45 +++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 37 insertions(+), 8 deletions(-)
+
+commit 35681b3edb79b1286f1aa0ece2f6ae99e0363190
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 16:02:55 2022 -0600
+
+    [benchmark-shape] Break lines and shape separately
+
+ perf/benchmark-shape.cc | 23 +++++++++++++++++------
+ 1 file changed, 17 insertions(+), 6 deletions(-)
+
+commit be1ac9c57232317647e59983e72b6a86f93151a2
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 15:55:19 2022 -0600
+
+    [benchmark-shape] Data-driven test sets
+
+ perf/benchmark-shape.cc | 78 ++++++++++++++++++++++++++++---------------------
+ 1 file changed, 44 insertions(+), 34 deletions(-)
+
+commit ae3efc64248f46478fe9ad3863a5dfb0a362fe5f
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 15:37:11 2022 -0600
+
+    [perf] Spawn off benchmark-shape from perf runner
+
+ perf/{perf-shaping.hh => benchmark-shape.cc} |  2 ++
+ perf/meson.build                             | 10 ++++++++++
+ perf/perf.cc                                 |  1 -
+ 3 files changed, 12 insertions(+), 1 deletion(-)
+
+commit 5f43ce825afbedb1edbbc6610d1c017aa0f5fe27
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 13:37:46 2022 -0600
+
+    [benchmark-set] Split SetLookup into an ordered and random version
+
+ perf/benchmark-set.cc | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+commit ae9c7b861b257897a7ff0044d38e70f95df3eec7
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 13:39:04 2022 -0600
+
+    [benchmark-set] At least increase needle by one in lookup benchmark
+
+ perf/benchmark-set.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 68a9b83d157c2c2ece2c49732f5bf68d843a77a8
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 13:27:42 2022 -0600
+
+    [benchmark-set] At least increase needle by one in lookup benchmark
+
+ perf/benchmark-set.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit b4236b7de6bb2823a4561357342309b0f6d7d264
+Author: Garret Rieger <grieger@google.com>
+Date:   Fri Apr 29 19:21:13 2022 +0000
+
+    [subset] Optimize Cmap4 collect_unicodes.
+    
+    Use set add_range() instead of individual add() calls.
+
+ src/hb-ot-cmap-table.hh | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+commit 5866ec05f5a2a613501095e1de64d641ad898021
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 13:14:41 2022 -0600
+
+    [benchmark-map] Remove rand() overhead from benchmark
+
+ perf/benchmark-map.cc | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+commit 067225a86d4309020b950661ef9de6cb0c51eb98
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 13:04:36 2022 -0600
+
+    [set] Optimize const page_for() using last_page_lookup caching
+    
+    Similar to previous commit.
+    
+    This speeds up SetLookup benchmark by 50%, but that's because that
+    lookup always hits the same page...
+
+ src/hb-bit-set.hh | 24 +++++++++++++++++++-----
+ 1 file changed, 19 insertions(+), 5 deletions(-)
+
+commit c283e41ce39bb3740417bed4f240cf625fb38cd4
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 12:45:48 2022 -0600
+
+    [set] Optimize non-const page_for() using last_page_lookup caching
+    
+    This speeds up SetOrderedInsert tests by 15 to 40 percent, and the
+    subset_mplus1p benchmarks by 9 to 27 percent.
+
+ src/hb-bit-set.hh | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+commit dd005911b955da49a11aa755acb9addc0c8a2a24
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Fri Apr 29 12:23:53 2022 -0600
+
+    [benchmark-set] Reduce lookup benchmark overhead
+    
+    Turnsout 90% was overhead...  Now lookup is in the 4ns ballpark.
+
+ perf/benchmark-set.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 4de5352a3d4f501b68907fa419a4fed70676e720
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Thu Apr 28 14:40:33 2022 -0600
+
+    [test] Add test
+    
+    From https://github.com/harfbuzz/harfbuzz/issues/3545
+    
+    Dropped the CFF table.
+
+ .../fonts/a59fd13f1525a91cbe529c882e93d9d1fbb80463.ttf   | Bin 0 -> 1180 bytes
+ test/shape/data/in-house/tests/context-matching.tests    |   1 +
+ 2 files changed, 1 insertion(+)
+
+commit d8292b8446b7875281a0d6fc8cb90e96b2f8d156
+Author: Behdad Esfahbod <behdad@behdad.org>
+Date:   Wed Apr 27 12:38:35 2022 -0600
+
+    [CFF] Fix parsing of empty Index
+    
+    https://github.com/harfbuzz/harfbuzz/issues/3545#issuecomment-1111047941
+
+ src/hb-ot-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 6454cec085ba51cefcd12b1f8027bc4a647347d5
+Author: David Corbett <corbett.dav@northeastern.edu>
+Date:   Sun Apr 24 11:10:17 2022 -0400
+
+    [USE] Classify U+10A38 as CONS_MOD_BELOW
+
+ src/gen-use-table.py                 | 3 +++
+ src/hb-ot-shape-complex-use-table.hh | 2 +-
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
 commit f7aee78e90bc53b3a95eb56d7550c9effe569ea2
 Author: Khaled Hosny <khaled@aliftype.com>
 Date:   Sun Apr 24 05:47:57 2022 +0200
diff --git a/source/libs/harfbuzz/harfbuzz-src/Makefile.am b/source/libs/harfbuzz/harfbuzz-src/Makefile.am
index 3055e5a77f1df43c2f39ede142d789d53bec6100..c14b4b7c20c3b9ad9eceb33351432954825137ea 100644
--- a/source/libs/harfbuzz/harfbuzz-src/Makefile.am
+++ b/source/libs/harfbuzz/harfbuzz-src/Makefile.am
@@ -4,7 +4,7 @@ NULL =
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = src util test docs
+SUBDIRS = src util test perf docs
 
 EXTRA_DIST = \
 	autogen.sh \
@@ -25,20 +25,6 @@ EXTRA_DIST = \
 	subprojects/google-benchmark.wrap \
 	subprojects/ragel.wrap \
 	subprojects/packagefiles/ragel/meson.build \
-	subprojects/ttf-parser.wrap \
-	perf/meson.build \
-	perf/perf-draw.hh \
-	perf/perf-extents.hh \
-	perf/perf-shaping.hh \
-	perf/perf.cc \
-	perf/fonts/Amiri-Regular.ttf \
-	perf/fonts/NotoNastaliqUrdu-Regular.ttf \
-	perf/fonts/NotoSansDevanagari-Regular.ttf \
-	perf/fonts/Roboto-Regular.ttf \
-	perf/texts/en-thelittleprince.txt \
-	perf/texts/en-words.txt \
-	perf/texts/fa-monologue.txt \
-	perf/texts/fa-thelittleprince.txt \
 	mingw-configure.sh \
 	$(NULL)
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/NEWS b/source/libs/harfbuzz/harfbuzz-src/NEWS
index 0559840cf352ad303feafc1c14c6fed71c89e220..b146f6a6f4955431c20c36d4f9391c8dd5c248c6 100644
--- a/source/libs/harfbuzz/harfbuzz-src/NEWS
+++ b/source/libs/harfbuzz/harfbuzz-src/NEWS
@@ -1,3 +1,61 @@
+Overview of changes leading to 4.4.1
+Wednesday, June 29, 2022
+====================================
+- Fix test failure with some compilers.
+- Fix Telugu and Kannada kerning regression.
+
+
+Overview of changes leading to 4.4.0
+Monday, June 27, 2022
+====================================
+- Caching of variable fonts shaping, in particular when using HarfBuzz’s own
+  font loading functions (ot). Bringing performance of variable shaping in par
+  with non-variable fonts shaping. (Behdad Esfahbod)
+- Caching of format 2 “Contextual Substitution” and “Chained Contexts
+  Substitution” lookups. Resulting in up to 20% speedup of lookup-heavy fonts
+  like Gulzar or Noto Nastaliq Urdu. (Behdad Esfahbod)
+- Improved ANSI output from hb-view. (Behdad Esfahbod)
+- Support for shaping legacy, pre-OpenType Windows 3.1-era, Arabic fonts that
+  relied on a fixed PUA encoding. (Khaled Hosny, Behdad Esfahbod)
+- Sinhala script is now shaped by the USE shaper instead of “indic” one.
+  (Behdad Esfahbod, David Corbett)
+- Thai shaper improvements. (David Corbett)
+- hb-ot-name API supports approximate BCP-47 language matching, for example
+  asking for “en_US” in a font that has only “en” names will return them.
+  (Behdad Esfahbod)
+- Optimized TrueType glyph shape loading. (Behdad Esfahbod)
+- Fix subsetting of HarfBuzz faces created via hb_face_create_for_tables().
+  (Garret Rieger)
+- Add 32 bit var store support to the subsetter. (Garret Rieger)
+
+- New API
++HB_BUFFER_FLAG_DEFINED
++HB_BUFFER_SERIALIZE_FLAG_DEFINED
++hb_font_changed()
++hb_font_get_serial()
++hb_ft_hb_font_changed()
++hb_set_hash()
++hb_map_copy()
++hb_map_hash()
+
+
+Overview of changes leading to 4.3.0
+Friday, May 20, 2022
+====================================
+- Major speed up in loading and subsetting fonts, especially in
+  handling CFF table. Subsetting some fonts is now 3 times faster.
+  (Behdad Esfahbod, Garret Rieger)
+- Speed up blending CFF2 table. (Behdad Esfahbod)
+- Speed up hb_ot_tags_from_language(). (Behdad Esfahbod, David Corbett)
+- Fix USE classification of U+10A38 to fix multiple marks on single Kharoshthi
+  base. (David Corbett)
+- Fix parsing of empty CFF Index. (Behdad Esfahbod)
+- Fix subsetting CPAL table with partial palette overlaps. (Garret Rieger)
+
+- New API
++hb_map_is_equal() (Behdad Esfahbod)
+
+
 Overview of changes leading to 4.2.1
 Sunday, April 24, 2022
 ====================================
diff --git a/source/libs/harfbuzz/harfbuzz-src/README b/source/libs/harfbuzz/harfbuzz-src/README
index 84c542f8ade4fb123f637a3d1a263b14a6eed089..4202961e04a0098a8b627592feed5daae246fa25 100644
--- a/source/libs/harfbuzz/harfbuzz-src/README
+++ b/source/libs/harfbuzz/harfbuzz-src/README
@@ -1,15 +1,99 @@
-This is HarfBuzz, a text shaping library.
+[![Linux CI Status](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg)](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg)
+[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main)
+[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz)
+[![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://www.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=harfbuzz/harfbuzz&amp;utm_campaign=Badge_Grade)
+[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
+[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
+
+# HarfBuzz
+
+HarfBuzz is a text shaping engine. It primarily supports [OpenType][1], but also
+[Apple Advanced Typography][2]. HarfBuzz is used in Android, Chrome,
+ChromeOS, Firefox, GNOME, GTK+, KDE, LibreOffice, OpenJDK, PlayStation, Qt,
+XeTeX, and other places.
 
 For bug reports, mailing list, and other information please visit:
 
   http://harfbuzz.org/
 
-For license information, see https://github.com/harfbuzz/harfbuzz/blob/main/COPYING
+For license information, see [COPYING](COPYING).
+
+## Documentation
+
+For user manual as well as API documentation, check: https://harfbuzz.github.io
+
+## Download
+
+For tarball releases of HarfBuzz, look [here][3]. At the same place you
+will also find Win32/Win64 binary bundles that include libharfbuzz DLL,
+hb-view.exe, hb-shape.exe, and all dependencies.
+
+The canonical source tree is available on [github][4].
+
+The API that comes with `hb.h` will not change incompatibly. Other, peripheral,
+headers are more likely to go through minor modifications, but again, we do our
+best to never change API in an incompatible way. We will never break the ABI.
+
+If you are not sure whether Pango or HarfBuzz is right for you, read [Pango vs
+HarfBuzz][5].
+
+## Development
+
+For build information, see [BUILD.md](BUILD.md).
+
+For custom configurations, see [CONFIG.md](CONFIG.md).
+
+For testing and profiling, see [TESTING.md](TESTING.md).
+
+To get a better idea of where HarfBuzz stands in the text rendering stack you
+may want to read [State of Text Rendering][6], though, that document is many
+years old. Here are a few presentation slides about HarfBuzz at the
+Internationalization and Unicode Conference over the years:
+
+*   November 2014, [Unicode, OpenType, and HarfBuzz: Closing the Circle][7],
+*   October 2012, [HarfBuzz, The Free and Open Text Shaping Engine][8],
+*   October 2009, [HarfBuzz: the Free and Open Shaping Engine][9].
+
+Both development and user support discussion around HarfBuzz happens on the
+[github][4].
+
+To report bugs or submit patches please use [github][4] issues and
+pull-requests.
+
+For a comparison of old vs new HarfBuzz memory consumption see [this][10].
+
+<!--See past and upcoming [HarfBuzz Hackfests](https://freedesktop.org/wiki/Software/HarfBuzz/Hackfests/)!-->
+
+## Name
+
+HarfBuzz (حرف‌باز) is my Persian translation of “[OpenType][1]”,
+transliterated using the Latin script. It sports a second meaning, but that
+ain’t translatable.
+
+> Background: Originally there was this font format called TrueType. People and
+> companies started calling their type engines all things ending in Type:
+> FreeType, CoolType, ClearType, etc. And then came OpenType, which is the
+> successor of TrueType. So, for my OpenType implementation, I decided to stick
+> with the concept but use the Persian translation. Which is fitting given that
+> Persian is written in the Arabic script, and OpenType is an extension of
+> TrueType that adds support for complex script rendering, and HarfBuzz is an
+> implementation of OpenType complex text shaping.
 
-For build information, see https://github.com/harfbuzz/harfbuzz/blob/main/BUILD.md
+<details>
+  <summary>Packaging status of HarfBuzz</summary>
 
-For custom configurations, see https://github.com/harfbuzz/harfbuzz/blob/main/CONFIG.md
+[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions)
 
-For test execution, see https://github.com/harfbuzz/harfbuzz/blob/main/TESTING.md
+</details>
 
-Documentation: https://harfbuzz.github.io
+[1]: https://docs.microsoft.com/en-us/typography/opentype/spec/
+[2]: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
+[3]: https://github.com/harfbuzz/harfbuzz/releases
+[4]: https://github.com/harfbuzz/harfbuzz
+[5]: http://mces.blogspot.com/2009/11/pango-vs-harfbuzz.html
+[6]: http://behdad.org/text/
+[7]: https://goo.gl/FSIQuC
+[8]: https://goo.gl/2wSRu
+[9]: http://behdad.org/download/Presentations/slippy/harfbuzz_slides.pdf
+[10]: https://goo.gl/woyty
diff --git a/source/libs/harfbuzz/harfbuzz-src/README.md b/source/libs/harfbuzz/harfbuzz-src/README.md
index fc12c65568c25e316cc260af9632f6567b42a194..4202961e04a0098a8b627592feed5daae246fa25 100644
--- a/source/libs/harfbuzz/harfbuzz-src/README.md
+++ b/source/libs/harfbuzz/harfbuzz-src/README.md
@@ -6,7 +6,12 @@
 [![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
 [![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
 
-This is HarfBuzz, a text shaping library.
+# HarfBuzz
+
+HarfBuzz is a text shaping engine. It primarily supports [OpenType][1], but also
+[Apple Advanced Typography][2]. HarfBuzz is used in Android, Chrome,
+ChromeOS, Firefox, GNOME, GTK+, KDE, LibreOffice, OpenJDK, PlayStation, Qt,
+XeTeX, and other places.
 
 For bug reports, mailing list, and other information please visit:
 
@@ -14,14 +19,66 @@ For bug reports, mailing list, and other information please visit:
 
 For license information, see [COPYING](COPYING).
 
+## Documentation
+
+For user manual as well as API documentation, check: https://harfbuzz.github.io
+
+## Download
+
+For tarball releases of HarfBuzz, look [here][3]. At the same place you
+will also find Win32/Win64 binary bundles that include libharfbuzz DLL,
+hb-view.exe, hb-shape.exe, and all dependencies.
+
+The canonical source tree is available on [github][4].
+
+The API that comes with `hb.h` will not change incompatibly. Other, peripheral,
+headers are more likely to go through minor modifications, but again, we do our
+best to never change API in an incompatible way. We will never break the ABI.
+
+If you are not sure whether Pango or HarfBuzz is right for you, read [Pango vs
+HarfBuzz][5].
+
+## Development
+
 For build information, see [BUILD.md](BUILD.md).
 
 For custom configurations, see [CONFIG.md](CONFIG.md).
 
-For test execution, see [TESTING.md](TESTING.md).
+For testing and profiling, see [TESTING.md](TESTING.md).
 
-Documentation: https://harfbuzz.github.io
+To get a better idea of where HarfBuzz stands in the text rendering stack you
+may want to read [State of Text Rendering][6], though, that document is many
+years old. Here are a few presentation slides about HarfBuzz at the
+Internationalization and Unicode Conference over the years:
 
+*   November 2014, [Unicode, OpenType, and HarfBuzz: Closing the Circle][7],
+*   October 2012, [HarfBuzz, The Free and Open Text Shaping Engine][8],
+*   October 2009, [HarfBuzz: the Free and Open Shaping Engine][9].
+
+Both development and user support discussion around HarfBuzz happens on the
+[github][4].
+
+To report bugs or submit patches please use [github][4] issues and
+pull-requests.
+
+For a comparison of old vs new HarfBuzz memory consumption see [this][10].
+
+<!--See past and upcoming [HarfBuzz Hackfests](https://freedesktop.org/wiki/Software/HarfBuzz/Hackfests/)!-->
+
+## Name
+
+HarfBuzz (حرف‌باز) is my Persian translation of “[OpenType][1]”,
+transliterated using the Latin script. It sports a second meaning, but that
+ain’t translatable.
+
+> Background: Originally there was this font format called TrueType. People and
+> companies started calling their type engines all things ending in Type:
+> FreeType, CoolType, ClearType, etc. And then came OpenType, which is the
+> successor of TrueType. So, for my OpenType implementation, I decided to stick
+> with the concept but use the Persian translation. Which is fitting given that
+> Persian is written in the Arabic script, and OpenType is an extension of
+> TrueType that adds support for complex script rendering, and HarfBuzz is an
+> implementation of OpenType complex text shaping.
 
 <details>
   <summary>Packaging status of HarfBuzz</summary>
@@ -29,3 +86,14 @@ Documentation: https://harfbuzz.github.io
 [![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions)
 
 </details>
+
+[1]: https://docs.microsoft.com/en-us/typography/opentype/spec/
+[2]: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
+[3]: https://github.com/harfbuzz/harfbuzz/releases
+[4]: https://github.com/harfbuzz/harfbuzz
+[5]: http://mces.blogspot.com/2009/11/pango-vs-harfbuzz.html
+[6]: http://behdad.org/text/
+[7]: https://goo.gl/FSIQuC
+[8]: https://goo.gl/2wSRu
+[9]: http://behdad.org/download/Presentations/slippy/harfbuzz_slides.pdf
+[10]: https://goo.gl/woyty
diff --git a/source/libs/harfbuzz/harfbuzz-src/TESTING.md b/source/libs/harfbuzz/harfbuzz-src/TESTING.md
index c722834559d390a374bb9a0be04b4305ff82531a..2fcc5c4f79ce7ffa3a634c6074f0247499813480 100644
--- a/source/libs/harfbuzz/harfbuzz-src/TESTING.md
+++ b/source/libs/harfbuzz/harfbuzz-src/TESTING.md
@@ -47,9 +47,5 @@ fuzzbuild/test/fuzzing/hb-subset-fuzzer test/fuzzing/fonts
 
 ## Profiling
 
-```
-meson build --reconfigure
-meson compile -C build
-build/perf/perf
-```
+For profiling, see `perf/README.md`.
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/configure.ac b/source/libs/harfbuzz/harfbuzz-src/configure.ac
index c353567d4bb653ff8a90869faec506189c6b146a..bc52d34f3f22144e28f780e7cabcc5dd019674b8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/configure.ac
+++ b/source/libs/harfbuzz/harfbuzz-src/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [4.2.1],
+        [4.4.1],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -374,7 +374,7 @@ if test "x$with_directwrite" = "xyes" -a "x$have_directwrite" != "xtrue"; then
 fi
 if $have_directwrite; then
 	DIRECTWRITE_CXXFLAGS=
-	DIRECTWRITE_LIBS=
+	DIRECTWRITE_LIBS=-ldwrite
 	AC_SUBST(DIRECTWRITE_CXXFLAGS)
 	AC_SUBST(DIRECTWRITE_LIBS)
 	AC_DEFINE(HAVE_DIRECTWRITE, 1, [Have DirectWrite library])
@@ -437,6 +437,8 @@ test/shape/data/text-rendering-tests/Makefile
 test/subset/Makefile
 test/subset/data/Makefile
 test/subset/data/repack_tests/Makefile
+test/threads/Makefile
+perf/Makefile
 docs/Makefile
 docs/version.xml
 ])
diff --git a/source/libs/harfbuzz/harfbuzz-src/harfbuzz.doap b/source/libs/harfbuzz/harfbuzz-src/harfbuzz.doap
index 07699697fefc20570ed35aaa230bb0fb9d915c58..2a5c0e62e6e51202790b44cf2272e86bd3654286 100644
--- a/source/libs/harfbuzz/harfbuzz-src/harfbuzz.doap
+++ b/source/libs/harfbuzz/harfbuzz-src/harfbuzz.doap
@@ -7,11 +7,11 @@
   <shortdesc xml:lang="en">Text shaping library</shortdesc>
 
   <homepage
-  rdf:resource="http://harfbuzz.org/" />
+  rdf:resource="https://github.com/harfbuzz/harfbuzz" />
   <mailing-list
-  rdf:resource="http://lists.freedesktop.org/mailman/listinfo/harfbuzz" />
-  <!--download-page
-  rdf:resource=""/-->
+  rdf:resource="https://github.com/harfbuzz/harfbuzz/discussions" />
+  <download-page
+  rdf:resource="https://github.com/harfbuzz/harfbuzz/releases" />
   <bug-database
   rdf:resource="https://github.com/harfbuzz/harfbuzz/issues" />
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/meson.build b/source/libs/harfbuzz/harfbuzz-src/meson.build
index ed59489bea7a5114a4fd749b5db96bb7c8f15d56..4a69d6d9ec3424884f6281b34968aa65096c8431 100644
--- a/source/libs/harfbuzz/harfbuzz-src/meson.build
+++ b/source/libs/harfbuzz/harfbuzz-src/meson.build
@@ -1,6 +1,6 @@
 project('harfbuzz', 'c', 'cpp',
   meson_version: '>= 0.55.0',
-  version: '4.2.1',
+  version: '4.4.1',
   default_options: [
     'cpp_rtti=false',       # Just to support msvc, we are passing -fno-exceptions also anyway
     'cpp_std=c++11',
diff --git a/source/libs/harfbuzz/harfbuzz-src/mingw-configure.sh b/source/libs/harfbuzz/harfbuzz-src/mingw-configure.sh
index 3281ce38299c6553ac9c42f4f301d4f1abc01683..496cae29140d7612897b2f247ccdee4e3e0c17db 100644
--- a/source/libs/harfbuzz/harfbuzz-src/mingw-configure.sh
+++ b/source/libs/harfbuzz/harfbuzz-src/mingw-configure.sh
@@ -17,12 +17,14 @@ exec "$(dirname "$0")"/configure \
 	CPP= \
 	LD= \
 	CFLAGS="-static-libgcc" \
-	CXXFLAGS="-static-libgcc -static-libstdc++" \
+	CXXFLAGS="-O2 -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-gdi \
 	--with-uniscribe \
+	--with-directwrite=auto \
 	"$@"
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/Makefile.am b/source/libs/harfbuzz/harfbuzz-src/src/Makefile.am
index 99168c6b7c5c05012a71fea174fd0f6ecd07582f..e1b56a5e9a7dcf703c2173f4dad03430ad782819 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/Makefile.am
+++ b/source/libs/harfbuzz/harfbuzz-src/src/Makefile.am
@@ -437,6 +437,7 @@ endif
 
 TESTS_ENVIRONMENT = \
 	srcdir="$(srcdir)" \
+	base_srcdir="$(srcdir)" \
 	builddir="$(builddir)" \
 	MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
 	HBSOURCES="$(HBSOURCES)" \
@@ -465,7 +466,7 @@ INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
 INTROSPECTION_SCANNER_ENV = CC="$(CC)"
 
 HarfBuzz-0.0.gir: libharfbuzz.la libharfbuzz-gobject.la
-HarfBuzz_0_0_gir_INCLUDES = GObject-2.0
+HarfBuzz_0_0_gir_INCLUDES = GObject-2.0 freetype2-2.0
 HarfBuzz_0_0_gir_CFLAGS = \
 	$(INCLUDES) \
 	$(HBCFLAGS) \
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/Makefile.sources b/source/libs/harfbuzz/harfbuzz-src/src/Makefile.sources
index 20c5188e21f5f76aade6d59036e1cd7797546e68..7211fb0d5f220c544bccfbda99a87b64ff10863e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/Makefile.sources
+++ b/source/libs/harfbuzz/harfbuzz-src/src/Makefile.sources
@@ -88,6 +88,15 @@ HB_BASE_sources = \
 	hb-ot-layout-gdef-table.hh \
 	hb-ot-layout-gpos-table.hh \
 	hb-ot-layout-gsub-table.hh \
+	OT/glyf/glyf.hh \
+	OT/glyf/glyf-helpers.hh \
+	OT/glyf/loca.hh \
+	OT/glyf/path-builder.hh \
+	OT/glyf/Glyph.hh \
+	OT/glyf/GlyphHeader.hh \
+	OT/glyf/SimpleGlyph.hh \
+	OT/glyf/CompositeGlyph.hh \
+	OT/glyf/SubsetGlyph.hh \
 	OT/Layout/GSUB/Common.hh \
 	OT/Layout/GSUB/Sequence.hh \
 	OT/Layout/GSUB/SingleSubstFormat1.hh \
@@ -110,6 +119,35 @@ HB_BASE_sources = \
 	OT/Layout/GSUB/SubstLookupSubTable.hh \
 	OT/Layout/GSUB/SubstLookup.hh \
 	OT/Layout/GSUB/GSUB.hh \
+	OT/Layout/GPOS.hh \
+	OT/Layout/GPOS/CursivePosFormat1.hh \
+	OT/Layout/GPOS/MarkLigPos.hh \
+	OT/Layout/GPOS/PairPos.hh \
+	OT/Layout/GPOS/Anchor.hh \
+	OT/Layout/GPOS/AnchorFormat1.hh \
+	OT/Layout/GPOS/MarkLigPosFormat1.hh \
+	OT/Layout/GPOS/PairPosFormat1.hh \
+	OT/Layout/GPOS/ExtensionPos.hh \
+	OT/Layout/GPOS/ChainContextPos.hh \
+	OT/Layout/GPOS/Common.hh \
+	OT/Layout/GPOS/ValueFormat.hh \
+	OT/Layout/GPOS/AnchorMatrix.hh \
+	OT/Layout/GPOS/MarkBasePosFormat1.hh \
+	OT/Layout/GPOS/AnchorFormat3.hh \
+	OT/Layout/GPOS/PosLookup.hh \
+	OT/Layout/GPOS/MarkMarkPos.hh \
+	OT/Layout/GPOS/PairPosFormat2.hh \
+	OT/Layout/GPOS/MarkBasePos.hh \
+	OT/Layout/GPOS/MarkMarkPosFormat1.hh \
+	OT/Layout/GPOS/SinglePos.hh \
+	OT/Layout/GPOS/MarkArray.hh \
+	OT/Layout/GPOS/CursivePos.hh \
+	OT/Layout/GPOS/PosLookupSubTable.hh \
+	OT/Layout/GPOS/MarkRecord.hh \
+	OT/Layout/GPOS/AnchorFormat2.hh \
+	OT/Layout/GPOS/ContextPos.hh \
+	OT/Layout/GPOS/SinglePosFormat2.hh \
+	OT/Layout/GPOS/SinglePosFormat1.hh \
 	hb-ot-layout-gsubgpos.hh \
 	hb-ot-layout-jstf-table.hh \
 	hb-ot-layout.cc \
@@ -131,30 +169,29 @@ HB_BASE_sources = \
 	hb-ot-os2-unicode-ranges.hh \
 	hb-ot-post-macroman.hh \
 	hb-ot-post-table.hh \
-	hb-ot-shape-complex-arabic-fallback.hh \
-	hb-ot-shape-complex-arabic-joining-list.hh \
-	hb-ot-shape-complex-arabic-table.hh \
-	hb-ot-shape-complex-arabic-win1256.hh \
-	hb-ot-shape-complex-arabic.cc \
-	hb-ot-shape-complex-arabic.hh \
-	hb-ot-shape-complex-default.cc \
-	hb-ot-shape-complex-hangul.cc \
-	hb-ot-shape-complex-hebrew.cc \
-	hb-ot-shape-complex-indic-table.cc \
-	hb-ot-shape-complex-indic.cc \
-	hb-ot-shape-complex-indic.hh \
-	hb-ot-shape-complex-khmer.cc \
-	hb-ot-shape-complex-khmer.hh \
-	hb-ot-shape-complex-myanmar.cc \
-	hb-ot-shape-complex-myanmar.hh \
-	hb-ot-shape-complex-syllabic.cc \
-	hb-ot-shape-complex-syllabic.hh \
-	hb-ot-shape-complex-thai.cc \
-	hb-ot-shape-complex-use-table.hh \
-	hb-ot-shape-complex-use.cc \
-	hb-ot-shape-complex-vowel-constraints.cc \
-	hb-ot-shape-complex-vowel-constraints.hh \
-	hb-ot-shape-complex.hh \
+	hb-ot-shaper-arabic-fallback.hh \
+	hb-ot-shaper-arabic-joining-list.hh \
+	hb-ot-shaper-arabic-pua.hh \
+	hb-ot-shaper-arabic-table.hh \
+	hb-ot-shaper-arabic-win1256.hh \
+	hb-ot-shaper-arabic.cc \
+	hb-ot-shaper-arabic.hh \
+	hb-ot-shaper-default.cc \
+	hb-ot-shaper-hangul.cc \
+	hb-ot-shaper-hebrew.cc \
+	hb-ot-shaper-indic-table.cc \
+	hb-ot-shaper-indic.cc \
+	hb-ot-shaper-indic.hh \
+	hb-ot-shaper-khmer.cc \
+	hb-ot-shaper-myanmar.cc \
+	hb-ot-shaper-syllabic.cc \
+	hb-ot-shaper-syllabic.hh \
+	hb-ot-shaper-thai.cc \
+	hb-ot-shaper-use-table.hh \
+	hb-ot-shaper-use.cc \
+	hb-ot-shaper-vowel-constraints.cc \
+	hb-ot-shaper-vowel-constraints.hh \
+	hb-ot-shaper.hh \
 	hb-ot-shape-fallback.cc \
 	hb-ot-shape-fallback.hh \
 	hb-ot-shape-normalize.cc \
@@ -203,19 +240,19 @@ HB_BASE_RAGEL_GENERATED_sources = \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
 	hb-number-parser.hh \
-	hb-ot-shape-complex-indic-machine.hh \
-	hb-ot-shape-complex-khmer-machine.hh \
-	hb-ot-shape-complex-myanmar-machine.hh \
-	hb-ot-shape-complex-use-machine.hh \
+	hb-ot-shaper-indic-machine.hh \
+	hb-ot-shaper-khmer-machine.hh \
+	hb-ot-shaper-myanmar-machine.hh \
+	hb-ot-shaper-use-machine.hh \
 	$(NULL)
 HB_BASE_RAGEL_sources = \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
 	hb-number-parser.rl \
-	hb-ot-shape-complex-indic-machine.rl \
-	hb-ot-shape-complex-khmer-machine.rl \
-	hb-ot-shape-complex-myanmar-machine.rl \
-	hb-ot-shape-complex-use-machine.rl \
+	hb-ot-shaper-indic-machine.rl \
+	hb-ot-shaper-khmer-machine.rl \
+	hb-ot-shaper-myanmar-machine.rl \
+	hb-ot-shaper-use-machine.rl \
 	$(NULL)
 
 HB_BASE_headers = \
@@ -224,6 +261,7 @@ HB_BASE_headers = \
 	hb-blob.h \
 	hb-buffer.h \
 	hb-common.h \
+	hb-cplusplus.hh \
 	hb-deprecated.h \
 	hb-draw.h \
 	hb-face.h \
@@ -301,6 +339,8 @@ HB_SUBSET_sources = \
 	hb-subset.cc \
 	hb-subset.hh \
 	hb-repacker.hh \
+	graph/graph.hh \
+	graph/serialize.hh \
 	$(NULL)
 
 HB_SUBSET_headers = \
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS.hh
new file mode 100644
index 0000000000000000000000000000000000000000..224d6b746bdaf40212d758adf1d6daee20fe3df8
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS.hh
@@ -0,0 +1,165 @@
+#ifndef OT_LAYOUT_GPOS_HH
+#define OT_LAYOUT_GPOS_HH
+
+#include "../../hb-ot-layout-common.hh"
+#include "../../hb-ot-layout-gsubgpos.hh"
+#include "GPOS/Common.hh"
+#include "GPOS/PosLookup.hh"
+
+namespace OT {
+namespace Layout {
+
+static void
+propagate_attachment_offsets (hb_glyph_position_t *pos,
+                              unsigned int len,
+                              unsigned int i,
+                              hb_direction_t direction,
+                              unsigned nesting_level = HB_MAX_NESTING_LEVEL);
+
+/*
+ * GPOS -- Glyph Positioning
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
+ */
+
+struct GPOS : GSUBGPOS
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS;
+
+  using Lookup = GPOS_impl::PosLookup;
+
+  const GPOS_impl::PosLookup& get_lookup (unsigned int i) const
+  { return static_cast<const GPOS_impl::PosLookup &> (GSUBGPOS::get_lookup (i)); }
+
+  static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features);
+    return GSUBGPOS::subset<GPOS_impl::PosLookup> (&l);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  { return GSUBGPOS::sanitize<GPOS_impl::PosLookup> (c); }
+
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++)
+    {
+      if (!c->gpos_lookups->has (i)) continue;
+      const GPOS_impl::PosLookup &l = get_lookup (i);
+      l.dispatch (c);
+    }
+  }
+
+  void closure_lookups (hb_face_t      *face,
+                        const hb_set_t *glyphs,
+                        hb_set_t       *lookup_indexes /* IN/OUT */) const
+  { GSUBGPOS::closure_lookups<GPOS_impl::PosLookup> (face, glyphs, lookup_indexes); }
+
+  typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
+};
+
+
+static void
+propagate_attachment_offsets (hb_glyph_position_t *pos,
+                              unsigned int len,
+                              unsigned int i,
+                              hb_direction_t direction,
+                              unsigned nesting_level)
+{
+  /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate
+   * offset of glyph they are attached to. */
+  int chain = pos[i].attach_chain(), type = pos[i].attach_type();
+  if (likely (!chain))
+    return;
+
+  pos[i].attach_chain() = 0;
+
+  unsigned int j = (int) i + chain;
+
+  if (unlikely (j >= len))
+    return;
+
+  if (unlikely (!nesting_level))
+    return;
+
+  propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1);
+
+  assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE));
+
+  if (type & GPOS_impl::ATTACH_TYPE_CURSIVE)
+  {
+    if (HB_DIRECTION_IS_HORIZONTAL (direction))
+      pos[i].y_offset += pos[j].y_offset;
+    else
+      pos[i].x_offset += pos[j].x_offset;
+  }
+  else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/
+  {
+    pos[i].x_offset += pos[j].x_offset;
+    pos[i].y_offset += pos[j].y_offset;
+
+    assert (j < i);
+    if (HB_DIRECTION_IS_FORWARD (direction))
+      for (unsigned int k = j; k < i; k++) {
+        pos[i].x_offset -= pos[k].x_advance;
+        pos[i].y_offset -= pos[k].y_advance;
+      }
+    else
+      for (unsigned int k = j + 1; k < i + 1; k++) {
+        pos[i].x_offset += pos[k].x_advance;
+        pos[i].y_offset += pos[k].y_advance;
+      }
+  }
+}
+
+void
+GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0;
+}
+
+void
+GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
+{
+  //_hb_buffer_assert_gsubgpos_vars (buffer);
+}
+
+void
+GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
+{
+  _hb_buffer_assert_gsubgpos_vars (buffer);
+
+  unsigned int len;
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
+  hb_direction_t direction = buffer->props.direction;
+
+  /* Handle attachments */
+  if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
+    for (unsigned i = 0; i < len; i++)
+      propagate_attachment_offsets (pos, len, i, direction);
+
+  if (unlikely (font->slant))
+  {
+    for (unsigned i = 0; i < len; i++)
+      if (unlikely (pos[i].y_offset))
+        pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset);
+  }
+}
+
+}
+
+struct GPOS_accelerator_t : Layout::GPOS::accelerator_t {
+  GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {}
+};
+
+}
+
+#endif  /* OT_LAYOUT_GPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/Anchor.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/Anchor.hh
new file mode 100644
index 0000000000000000000000000000000000000000..bfe6b36afd34ba633fabcb239bc017a1bcf49cbe
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/Anchor.hh
@@ -0,0 +1,84 @@
+#ifndef OT_LAYOUT_GPOS_ANCHOR_HH
+#define OT_LAYOUT_GPOS_ANCHOR_HH
+
+#include "AnchorFormat1.hh"
+#include "AnchorFormat2.hh"
+#include "AnchorFormat3.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct Anchor
+{
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  AnchorFormat1         format1;
+  AnchorFormat2         format2;
+  AnchorFormat3         format3;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 3: return_trace (u.format3.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
+                   float *x, float *y) const
+  {
+    *x = *y = 0;
+    switch (u.format) {
+    case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
+    case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
+    case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
+    default:                                          return;
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    switch (u.format) {
+    case 1: return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer))));
+    case 2:
+      if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+      {
+        // AnchorFormat 2 just containins extra hinting information, so
+        // if hints are being dropped convert to format 1.
+        return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer))));
+      }
+      return_trace (bool (reinterpret_cast<Anchor *> (u.format2.copy (c->serializer))));
+    case 3: return_trace (bool (reinterpret_cast<Anchor *> (u.format3.copy (c->serializer,
+                                                                            c->plan->layout_variation_idx_map))));
+    default:return_trace (false);
+    }
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    switch (u.format) {
+    case 1: case 2:
+      return;
+    case 3:
+      u.format3.collect_variation_indices (c);
+      return;
+    default: return;
+    }
+  }
+};
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_ANCHOR_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..738cc31bbfa4148a01bc4d3e84b8fa12e0e8373a
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat1.hh
@@ -0,0 +1,46 @@
+#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT1_HH
+#define OT_LAYOUT_GPOS_ANCHORFORMAT1_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct AnchorFormat1
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  FWORD         xCoordinate;            /* Horizontal value--in design units */
+  FWORD         yCoordinate;            /* Vertical value--in design units */
+  public:
+  DEFINE_SIZE_STATIC (6);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
+                   float *x, float *y) const
+  {
+    hb_font_t *font = c->font;
+    *x = font->em_fscale_x (xCoordinate);
+    *y = font->em_fscale_y (yCoordinate);
+  }
+
+  AnchorFormat1* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    AnchorFormat1* out = c->embed<AnchorFormat1> (this);
+    if (!out) return_trace (out);
+    out->format = 1;
+    return_trace (out);
+  }
+};
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_ANCHORFORMAT1_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat2.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat2.hh
new file mode 100644
index 0000000000000000000000000000000000000000..70b4d19f53c924026498f07b9fe81ca43ccb2732
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat2.hh
@@ -0,0 +1,58 @@
+#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT2_HH
+#define OT_LAYOUT_GPOS_ANCHORFORMAT2_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct AnchorFormat2
+{
+
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 2 */
+  FWORD         xCoordinate;            /* Horizontal value--in design units */
+  FWORD         yCoordinate;            /* Vertical value--in design units */
+  HBUINT16      anchorPoint;            /* Index to glyph contour point */
+  public:
+  DEFINE_SIZE_STATIC (8);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
+                   float *x, float *y) const
+  {
+    hb_font_t *font = c->font;
+
+#ifdef HB_NO_HINTING
+    *x = font->em_fscale_x (xCoordinate);
+    *y = font->em_fscale_y (yCoordinate);
+    return;
+#endif
+
+    unsigned int x_ppem = font->x_ppem;
+    unsigned int y_ppem = font->y_ppem;
+    hb_position_t cx = 0, cy = 0;
+    bool ret;
+
+    ret = (x_ppem || y_ppem) &&
+          font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
+    *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate);
+    *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate);
+  }
+
+  AnchorFormat2* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<AnchorFormat2> (this));
+  }
+};
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_ANCHORFORMAT2_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat3.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat3.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d77b4699be9517a848e53a2179b8df29bae01d48
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorFormat3.hh
@@ -0,0 +1,70 @@
+#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
+#define OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct AnchorFormat3
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 3 */
+  FWORD         xCoordinate;            /* Horizontal value--in design units */
+  FWORD         yCoordinate;            /* Vertical value--in design units */
+  Offset16To<Device>
+                xDeviceTable;           /* Offset to Device table for X
+                                         * coordinate-- from beginning of
+                                         * Anchor table (may be NULL) */
+  Offset16To<Device>
+                yDeviceTable;           /* Offset to Device table for Y
+                                         * coordinate-- from beginning of
+                                         * Anchor table (may be NULL) */
+  public:
+  DEFINE_SIZE_STATIC (10);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
+  }
+
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
+                   float *x, float *y) const
+  {
+    hb_font_t *font = c->font;
+    *x = font->em_fscale_x (xCoordinate);
+    *y = font->em_fscale_y (yCoordinate);
+
+    if (font->x_ppem || font->num_coords)
+      *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
+    if (font->y_ppem || font->num_coords)
+      *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
+  }
+
+  AnchorFormat3* copy (hb_serialize_context_t *c,
+                       const hb_map_t *layout_variation_idx_map) const
+  {
+    TRACE_SERIALIZE (this);
+    if (!layout_variation_idx_map) return_trace (nullptr);
+
+    auto *out = c->embed<AnchorFormat3> (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
+    out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
+    return_trace (out);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices);
+    (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices);
+  }
+};
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorMatrix.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorMatrix.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c442efa1eaa89b47be0654824b368e7e0f2bd321
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/AnchorMatrix.hh
@@ -0,0 +1,77 @@
+#ifndef OT_LAYOUT_GPOS_ANCHORMATRIX_HH
+#define OT_LAYOUT_GPOS_ANCHORMATRIX_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct AnchorMatrix
+{
+  HBUINT16      rows;                   /* Number of rows */
+  UnsizedArrayOf<Offset16To<Anchor>>
+                matrixZ;                /* Matrix of offsets to Anchor tables--
+                                         * from beginning of AnchorMatrix table */
+  public:
+  DEFINE_SIZE_ARRAY (2, matrixZ);
+
+  bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
+  {
+    TRACE_SANITIZE (this);
+    if (!c->check_struct (this)) return_trace (false);
+    if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
+    unsigned int count = rows * cols;
+    if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
+    for (unsigned int i = 0; i < count; i++)
+      if (!matrixZ[i].sanitize (c, this)) return_trace (false);
+    return_trace (true);
+  }
+
+  const Anchor& get_anchor (unsigned int row, unsigned int col,
+                            unsigned int cols, bool *found) const
+  {
+    *found = false;
+    if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
+    *found = !matrixZ[row * cols + col].is_null ();
+    return this+matrixZ[row * cols + col];
+  }
+
+  template <typename Iterator,
+            hb_requires (hb_is_iterator (Iterator))>
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  Iterator index_iter) const
+  {
+    for (unsigned i : index_iter)
+      (this+matrixZ[i]).collect_variation_indices (c);
+  }
+
+  template <typename Iterator,
+      hb_requires (hb_is_iterator (Iterator))>
+  bool subset (hb_subset_context_t *c,
+               unsigned             num_rows,
+               Iterator             index_iter) const
+  {
+    TRACE_SUBSET (this);
+
+    auto *out = c->serializer->start_embed (this);
+
+    if (!index_iter) return_trace (false);
+    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
+
+    out->rows = num_rows;
+    for (const unsigned i : index_iter)
+    {
+      auto *offset = c->serializer->embed (matrixZ[i]);
+      if (!offset) return_trace (false);
+      offset->serialize_subset (c, matrixZ[i], this);
+    }
+
+    return_trace (true);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ChainContextPos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ChainContextPos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d551ac2a2be63fe93875742ec9124af825f0b00a
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ChainContextPos.hh
@@ -0,0 +1,14 @@
+#ifndef OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH
+#define OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct ChainContextPos : ChainContext {};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/Common.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/Common.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e16c06729d22951ed7a5ab3f175a7be162c3414c
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/Common.hh
@@ -0,0 +1,32 @@
+#ifndef OT_LAYOUT_GPOS_COMMON_HH
+#define OT_LAYOUT_GPOS_COMMON_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+enum attach_type_t {
+  ATTACH_TYPE_NONE      = 0X00,
+
+  /* Each attachment should be either a mark or a cursive; can't be both. */
+  ATTACH_TYPE_MARK      = 0X01,
+  ATTACH_TYPE_CURSIVE   = 0X02,
+};
+
+/* buffer **position** var allocations */
+#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */
+#define attach_type() var.u8[2] /* attachment type */
+/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */
+
+template<typename Iterator, typename SrcLookup>
+static void SinglePos_serialize (hb_serialize_context_t *c,
+                                 const SrcLookup *src,
+                                 Iterator it,
+                                 const hb_map_t *layout_variation_idx_map);
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_COMMON_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ContextPos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ContextPos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2a01eaa3a6824aea7ad8696c6474857ae7591a93
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ContextPos.hh
@@ -0,0 +1,14 @@
+#ifndef OT_LAYOUT_GPOS_CONTEXTPOS_HH
+#define OT_LAYOUT_GPOS_CONTEXTPOS_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct ContextPos : Context {};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_CONTEXTPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/CursivePos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/CursivePos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c105cfb0916504ceaa6fb7b1bc317cf7771f3fe9
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/CursivePos.hh
@@ -0,0 +1,35 @@
+#ifndef OT_LAYOUT_GPOS_CURSIVEPOS_HH
+#define OT_LAYOUT_GPOS_CURSIVEPOS_HH
+
+#include "CursivePosFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct CursivePos
+{
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  CursivePosFormat1     format1;
+  } u;
+
+  public:
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/CursivePosFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/CursivePosFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e212fab976b1f6ead0133337ee98c6c6e490bd9b
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/CursivePosFormat1.hh
@@ -0,0 +1,281 @@
+#ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH
+#define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH
+
+#include "Anchor.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct EntryExitRecord
+{
+  friend struct CursivePosFormat1;
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const void *src_base) const
+  {
+    (src_base+entryAnchor).collect_variation_indices (c);
+    (src_base+exitAnchor).collect_variation_indices (c);
+  }
+
+  EntryExitRecord* subset (hb_subset_context_t *c,
+                           const void *src_base) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->entryAnchor.serialize_subset (c, entryAnchor, src_base);
+    out->exitAnchor.serialize_subset (c, exitAnchor, src_base);
+    return_trace (out);
+  }
+
+  protected:
+  Offset16To<Anchor>
+                entryAnchor;            /* Offset to EntryAnchor table--from
+                                         * beginning of CursivePos
+                                         * subtable--may be NULL */
+  Offset16To<Anchor>
+                exitAnchor;             /* Offset to ExitAnchor table--from
+                                         * beginning of CursivePos
+                                         * subtable--may be NULL */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+static void
+reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) {
+  int chain = pos[i].attach_chain(), type = pos[i].attach_type();
+  if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
+    return;
+
+  pos[i].attach_chain() = 0;
+
+  unsigned int j = (int) i + chain;
+
+  /* Stop if we see new parent in the chain. */
+  if (j == new_parent)
+    return;
+
+  reverse_cursive_minor_offset (pos, j, direction, new_parent);
+
+  if (HB_DIRECTION_IS_HORIZONTAL (direction))
+    pos[j].y_offset = -pos[i].y_offset;
+  else
+    pos[j].x_offset = -pos[i].x_offset;
+
+  pos[j].attach_chain() = -chain;
+  pos[j].attach_type() = type;
+}
+
+
+struct CursivePosFormat1
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  Offset16To<Coverage>
+                coverage;               /* Offset to Coverage table--from
+                                         * beginning of subtable */
+  Array16Of<EntryExitRecord>
+                entryExitRecord;        /* Array of EntryExit records--in
+                                         * Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (6, entryExitRecord);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    + hb_zip (this+coverage, entryExitRecord)
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); })
+    ;
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
+
+  const Coverage &get_coverage () const { return this+coverage; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+
+    const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
+    if (!this_record.entryAnchor) return_trace (false);
+
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
+    unsigned unsafe_from;
+    if (!skippy_iter.prev (&unsafe_from))
+    {
+      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
+    if (!prev_record.exitAnchor)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    unsigned int i = skippy_iter.idx;
+    unsigned int j = buffer->idx;
+
+    buffer->unsafe_to_break (i, j);
+    float entry_x, entry_y, exit_x, exit_y;
+    (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
+    (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
+
+    hb_glyph_position_t *pos = buffer->pos;
+
+    hb_position_t d;
+    /* Main-direction adjustment */
+    switch (c->direction) {
+      case HB_DIRECTION_LTR:
+        pos[i].x_advance  = roundf (exit_x) + pos[i].x_offset;
+
+        d = roundf (entry_x) + pos[j].x_offset;
+        pos[j].x_advance -= d;
+        pos[j].x_offset  -= d;
+        break;
+      case HB_DIRECTION_RTL:
+        d = roundf (exit_x) + pos[i].x_offset;
+        pos[i].x_advance -= d;
+        pos[i].x_offset  -= d;
+
+        pos[j].x_advance  = roundf (entry_x) + pos[j].x_offset;
+        break;
+      case HB_DIRECTION_TTB:
+        pos[i].y_advance  = roundf (exit_y) + pos[i].y_offset;
+
+        d = roundf (entry_y) + pos[j].y_offset;
+        pos[j].y_advance -= d;
+        pos[j].y_offset  -= d;
+        break;
+      case HB_DIRECTION_BTT:
+        d = roundf (exit_y) + pos[i].y_offset;
+        pos[i].y_advance -= d;
+        pos[i].y_offset  -= d;
+
+        pos[j].y_advance  = roundf (entry_y);
+        break;
+      case HB_DIRECTION_INVALID:
+      default:
+        break;
+    }
+
+    /* Cross-direction adjustment */
+
+    /* We attach child to parent (think graph theory and rooted trees whereas
+     * the root stays on baseline and each node aligns itself against its
+     * parent.
+     *
+     * Optimize things for the case of RightToLeft, as that's most common in
+     * Arabic. */
+    unsigned int child  = i;
+    unsigned int parent = j;
+    hb_position_t x_offset = entry_x - exit_x;
+    hb_position_t y_offset = entry_y - exit_y;
+    if  (!(c->lookup_props & LookupFlag::RightToLeft))
+    {
+      unsigned int k = child;
+      child = parent;
+      parent = k;
+      x_offset = -x_offset;
+      y_offset = -y_offset;
+    }
+
+    /* If child was already connected to someone else, walk through its old
+     * chain and reverse the link direction, such that the whole tree of its
+     * previous connection now attaches to new parent.  Watch out for case
+     * where new parent is on the path from old chain...
+     */
+    reverse_cursive_minor_offset (pos, child, c->direction, parent);
+
+    pos[child].attach_type() = ATTACH_TYPE_CURSIVE;
+    pos[child].attach_chain() = (int) parent - (int) child;
+    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
+      pos[child].y_offset = y_offset;
+    else
+      pos[child].x_offset = x_offset;
+
+    /* If parent was attached to child, separate them.
+     * https://github.com/harfbuzz/harfbuzz/issues/2469
+     */
+    if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain()))
+      pos[parent].attach_chain() = 0;
+
+    buffer->idx++;
+    return_trace (true);
+  }
+
+  template <typename Iterator,
+            hb_requires (hb_is_iterator (Iterator))>
+  void serialize (hb_subset_context_t *c,
+                  Iterator it,
+                  const void *src_base)
+  {
+    if (unlikely (!c->serializer->extend_min ((*this)))) return;
+    this->format = 1;
+    this->entryExitRecord.len = it.len ();
+
+    for (const EntryExitRecord& entry_record : + it
+                                               | hb_map (hb_second))
+      entry_record.subset (c, src_base);
+
+    auto glyphs =
+    + it
+    | hb_map_retains_sorting (hb_first)
+    ;
+
+    coverage.serialize_serialize (c->serializer, glyphs);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    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);
+    if (unlikely (!out)) return_trace (false);
+
+    auto it =
+    + hb_zip (this+coverage, entryExitRecord)
+    | hb_filter (glyphset, hb_first)
+    | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&>
+                              { return hb_pair (glyph_map[p.first], p.second);})
+    ;
+
+    bool ret = bool (it);
+    out->serialize (c, it, this);
+    return_trace (ret);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ExtensionPos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ExtensionPos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d1808adab40de7ba21b82e9a7308f27b60326999
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ExtensionPos.hh
@@ -0,0 +1,17 @@
+#ifndef OT_LAYOUT_GPOS_EXTENSIONPOS_HH
+#define OT_LAYOUT_GPOS_EXTENSIONPOS_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct ExtensionPos : Extension<ExtensionPos>
+{
+  typedef struct PosLookupSubTable SubTable;
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_EXTENSIONPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkArray.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkArray.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f8cddd199186dfa6043ab09787411f6627507774
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkArray.hh
@@ -0,0 +1,113 @@
+#ifndef OT_LAYOUT_GPOS_MARKARRAY_HH
+#define OT_LAYOUT_GPOS_MARKARRAY_HH
+
+#include "AnchorMatrix.hh"
+#include "MarkRecord.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct MarkArray : Array16Of<MarkRecord>        /* Array of MarkRecords--in Coverage order */
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (Array16Of<MarkRecord>::sanitize (c, this));
+  }
+
+  bool apply (hb_ot_apply_context_t *c,
+              unsigned int mark_index, unsigned int glyph_index,
+              const AnchorMatrix &anchors, unsigned int class_count,
+              unsigned int glyph_pos) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    const MarkRecord &record = Array16Of<MarkRecord>::operator[](mark_index);
+    unsigned int mark_class = record.klass;
+
+    const Anchor& mark_anchor = this + record.markAnchor;
+    bool found;
+    const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
+    /* If this subtable doesn't have an anchor for this base and this class,
+     * return false such that the subsequent subtables have a chance at it. */
+    if (unlikely (!found)) return_trace (false);
+
+    float mark_x, mark_y, base_x, base_y;
+
+    buffer->unsafe_to_break (glyph_pos, buffer->idx + 1);
+    mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
+    glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
+
+    hb_glyph_position_t &o = buffer->cur_pos();
+    o.x_offset = roundf (base_x - mark_x);
+    o.y_offset = roundf (base_y - mark_y);
+    o.attach_type() = ATTACH_TYPE_MARK;
+    o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
+    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+
+    buffer->idx++;
+    return_trace (true);
+  }
+
+  template <typename Iterator,
+      hb_requires (hb_is_iterator (Iterator))>
+  bool subset (hb_subset_context_t *c,
+               Iterator             coverage,
+               const hb_map_t      *klass_mapping) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+
+    auto* out = c->serializer->start_embed (this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    auto mark_iter =
+    + hb_zip (coverage, this->iter ())
+    | hb_filter (glyphset, hb_first)
+    | hb_map (hb_second)
+    ;
+
+    unsigned new_length = 0;
+    for (const auto& mark_record : mark_iter) {
+      if (unlikely (!mark_record.subset (c, this, klass_mapping)))
+        return_trace (false);
+      new_length++;
+    }
+
+    if (unlikely (!c->serializer->check_assign (out->len, new_length,
+                                                HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)))
+      return_trace (false);
+
+    return_trace (true);
+  }
+};
+
+static void Markclass_closure_and_remap_indexes (const Coverage  &mark_coverage,
+                                                 const MarkArray &mark_array,
+                                                 const hb_set_t  &glyphset,
+                                                 hb_map_t*        klass_mapping /* INOUT */)
+{
+  hb_set_t orig_classes;
+
+  + hb_zip (mark_coverage, mark_array)
+  | hb_filter (glyphset, hb_first)
+  | hb_map (hb_second)
+  | hb_map (&MarkRecord::get_class)
+  | hb_sink (orig_classes)
+  ;
+
+  unsigned idx = 0;
+  for (auto klass : orig_classes.iter ())
+  {
+    if (klass_mapping->has (klass)) continue;
+    klass_mapping->set (klass, idx);
+    idx++;
+  }
+}
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKARRAY_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkBasePos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkBasePos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e99e13ff84f3df29d1669058625caa47a50066ff
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkBasePos.hh
@@ -0,0 +1,35 @@
+#ifndef OT_LAYOUT_GPOS_MARKBASEPOS_HH
+#define OT_LAYOUT_GPOS_MARKBASEPOS_HH
+
+#include "MarkBasePosFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct MarkBasePos
+{
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  MarkBasePosFormat1    format1;
+  } u;
+
+  public:
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKBASEPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkBasePosFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkBasePosFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a10b806fe5f7bb30d44d954d80e6c45fec83f9b3
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkBasePosFormat1.hh
@@ -0,0 +1,217 @@
+#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
+#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
+
+#include "MarkArray.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+typedef AnchorMatrix BaseArray;         /* base-major--
+                                         * in order of BaseCoverage Index--,
+                                         * mark-minor--
+                                         * ordered by class--zero-based. */
+
+struct MarkBasePosFormat1
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  Offset16To<Coverage>
+                markCoverage;           /* Offset to MarkCoverage table--from
+                                         * beginning of MarkBasePos subtable */
+  Offset16To<Coverage>
+                baseCoverage;           /* Offset to BaseCoverage table--from
+                                         * beginning of MarkBasePos subtable */
+  HBUINT16      classCount;             /* Number of classes defined for marks */
+  Offset16To<MarkArray>
+                markArray;              /* Offset to MarkArray table--from
+                                         * beginning of MarkBasePos subtable */
+  Offset16To<BaseArray>
+                baseArray;              /* Offset to BaseArray table--from
+                                         * beginning of MarkBasePos subtable */
+
+  public:
+  DEFINE_SIZE_STATIC (12);
+
+    bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  markCoverage.sanitize (c, this) &&
+                  baseCoverage.sanitize (c, this) &&
+                  markArray.sanitize (c, this) &&
+                  baseArray.sanitize (c, this, (unsigned int) classCount));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    return (this+markCoverage).intersects (glyphs) &&
+           (this+baseCoverage).intersects (glyphs);
+  }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    + hb_zip (this+markCoverage, this+markArray)
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); })
+    ;
+
+    hb_map_t klass_mapping;
+    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping);
+
+    unsigned basecount = (this+baseArray).rows;
+    auto base_iter =
+    + hb_zip (this+baseCoverage, hb_range (basecount))
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    ;
+
+    hb_sorted_vector_t<unsigned> base_indexes;
+    for (const unsigned row : base_iter)
+    {
+      + hb_range ((unsigned) classCount)
+      | hb_filter (klass_mapping)
+      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
+      | hb_sink (base_indexes)
+      ;
+    }
+    (this+baseArray).collect_variation_indices (c, base_indexes.iter ());
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return;
+    if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return;
+  }
+
+  const Coverage &get_coverage () const { return this+markCoverage; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
+    if (likely (mark_index == NOT_COVERED)) return_trace (false);
+
+    /* Now we search backwards for a non-mark glyph */
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
+    skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
+    do {
+      unsigned unsafe_from;
+      if (!skippy_iter.prev (&unsafe_from))
+      {
+        buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+        return_trace (false);
+      }
+
+      /* We only want to attach to the first of a MultipleSubst sequence.
+       * https://github.com/harfbuzz/harfbuzz/issues/740
+       * Reject others...
+       * ...but stop if we find a mark in the MultipleSubst sequence:
+       * https://github.com/harfbuzz/harfbuzz/issues/1020 */
+      if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
+          0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
+          (skippy_iter.idx == 0 ||
+           _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
+           _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
+           _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
+           _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
+           _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
+           ))
+        break;
+      skippy_iter.reject ();
+    } while (true);
+
+    /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
+    //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+
+    unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
+    if (base_index == NOT_COVERED)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    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);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    hb_map_t klass_mapping;
+    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
+
+    if (!klass_mapping.get_population ()) return_trace (false);
+    out->classCount = klass_mapping.get_population ();
+
+    auto mark_iter =
+    + hb_zip (this+markCoverage, this+markArray)
+    | hb_filter (glyphset, hb_first)
+    ;
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + mark_iter
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
+      return_trace (false);
+
+    out->markArray.serialize_subset (c, markArray, this,
+                                     (this+markCoverage).iter (),
+                                     &klass_mapping);
+
+    unsigned basecount = (this+baseArray).rows;
+    auto base_iter =
+    + hb_zip (this+baseCoverage, hb_range (basecount))
+    | hb_filter (glyphset, hb_first)
+    ;
+
+    new_coverage.reset ();
+    + base_iter
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
+      return_trace (false);
+
+    hb_sorted_vector_t<unsigned> base_indexes;
+    for (const unsigned row : + base_iter
+                              | hb_map (hb_second))
+    {
+      + hb_range ((unsigned) classCount)
+      | hb_filter (klass_mapping)
+      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
+      | hb_sink (base_indexes)
+      ;
+    }
+
+    out->baseArray.serialize_subset (c, baseArray, this,
+                                     base_iter.len (),
+                                     base_indexes.iter ());
+
+    return_trace (true);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkLigPos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkLigPos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..7e74aa73e0a39c432fd6eba396cda24dfbbb1e4f
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkLigPos.hh
@@ -0,0 +1,35 @@
+#ifndef OT_LAYOUT_GPOS_MARKLIGPOS_HH
+#define OT_LAYOUT_GPOS_MARKLIGPOS_HH
+
+#include "MarkLigPosFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct MarkLigPos
+{
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  MarkLigPosFormat1     format1;
+  } u;
+
+  public:
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKLIGPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkLigPosFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkLigPosFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4382aa6c6cb3d4a1e4f1304c8d5c8acf4d7dea7a
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkLigPosFormat1.hh
@@ -0,0 +1,244 @@
+#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
+#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+typedef AnchorMatrix LigatureAttach;    /* component-major--
+                                         * in order of writing direction--,
+                                         * mark-minor--
+                                         * ordered by class--zero-based. */
+
+/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
+struct LigatureArray : List16OfOffset16To<LigatureAttach>
+{
+  template <typename Iterator,
+            hb_requires (hb_is_iterator (Iterator))>
+  bool subset (hb_subset_context_t *c,
+               Iterator             coverage,
+               unsigned             class_count,
+               const hb_map_t      *klass_mapping) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+
+    auto *out = c->serializer->start_embed (this);
+    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
+
+    for (const auto _ : + hb_zip (coverage, *this)
+                  | hb_filter (glyphset, hb_first))
+    {
+      auto *matrix = out->serialize_append (c->serializer);
+      if (unlikely (!matrix)) return_trace (false);
+
+      const LigatureAttach& src = (this + _.second);
+      auto indexes =
+          + hb_range (src.rows * class_count)
+          | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
+          ;
+      matrix->serialize_subset (c,
+                                _.second,
+                                this,
+                                src.rows,
+                                indexes);
+    }
+    return_trace (this->len);
+  }
+};
+
+struct MarkLigPosFormat1
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  Offset16To<Coverage>
+                markCoverage;           /* Offset to Mark Coverage table--from
+                                         * beginning of MarkLigPos subtable */
+  Offset16To<Coverage>
+                ligatureCoverage;       /* Offset to Ligature Coverage
+                                         * table--from beginning of MarkLigPos
+                                         * subtable */
+  HBUINT16      classCount;             /* Number of defined mark classes */
+  Offset16To<MarkArray>
+                markArray;              /* Offset to MarkArray table--from
+                                         * beginning of MarkLigPos subtable */
+  Offset16To<LigatureArray>
+                ligatureArray;          /* Offset to LigatureArray table--from
+                                         * beginning of MarkLigPos subtable */
+  public:
+  DEFINE_SIZE_STATIC (12);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  markCoverage.sanitize (c, this) &&
+                  ligatureCoverage.sanitize (c, this) &&
+                  markArray.sanitize (c, this) &&
+                  ligatureArray.sanitize (c, this, (unsigned int) classCount));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    return (this+markCoverage).intersects (glyphs) &&
+           (this+ligatureCoverage).intersects (glyphs);
+  }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    + hb_zip (this+markCoverage, this+markArray)
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); })
+    ;
+
+    hb_map_t klass_mapping;
+    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping);
+
+    unsigned ligcount = (this+ligatureArray).len;
+    auto lig_iter =
+    + hb_zip (this+ligatureCoverage, hb_range (ligcount))
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    ;
+
+    const LigatureArray& lig_array = this+ligatureArray;
+    for (const unsigned i : lig_iter)
+    {
+      hb_sorted_vector_t<unsigned> lig_indexes;
+      unsigned row_count = lig_array[i].rows;
+      for (unsigned row : + hb_range (row_count))
+      {
+        + hb_range ((unsigned) classCount)
+        | hb_filter (klass_mapping)
+        | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
+        | hb_sink (lig_indexes)
+        ;
+      }
+
+      lig_array[i].collect_variation_indices (c, lig_indexes.iter ());
+    }
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return;
+    if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return;
+  }
+
+  const Coverage &get_coverage () const { return this+markCoverage; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
+    if (likely (mark_index == NOT_COVERED)) return_trace (false);
+
+    /* Now we search backwards for a non-mark glyph */
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
+    skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
+    unsigned unsafe_from;
+    if (!skippy_iter.prev (&unsafe_from))
+    {
+      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
+    //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+
+    unsigned int j = skippy_iter.idx;
+    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[j].codepoint);
+    if (lig_index == NOT_COVERED)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    const LigatureArray& lig_array = this+ligatureArray;
+    const LigatureAttach& lig_attach = lig_array[lig_index];
+
+    /* Find component to attach to */
+    unsigned int comp_count = lig_attach.rows;
+    if (unlikely (!comp_count))
+    {
+      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    /* We must now check whether the ligature ID of the current mark glyph
+     * is identical to the ligature ID of the found ligature.  If yes, we
+     * can directly use the component index.  If not, we attach the mark
+     * glyph to the last component of the ligature. */
+    unsigned int comp_index;
+    unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]);
+    unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
+    unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
+    if (lig_id && lig_id == mark_id && mark_comp > 0)
+      comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1;
+    else
+      comp_index = comp_count - 1;
+
+    return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    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);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    hb_map_t klass_mapping;
+    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
+
+    if (!klass_mapping.get_population ()) return_trace (false);
+    out->classCount = klass_mapping.get_population ();
+
+    auto mark_iter =
+    + hb_zip (this+markCoverage, this+markArray)
+    | hb_filter (glyphset, hb_first)
+    ;
+
+    auto new_mark_coverage =
+    + mark_iter
+    | hb_map_retains_sorting (hb_first)
+    | hb_map_retains_sorting (glyph_map)
+    ;
+
+    if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage))
+      return_trace (false);
+
+    out->markArray.serialize_subset (c, markArray, this,
+                                     (this+markCoverage).iter (),
+                                     &klass_mapping);
+
+    auto new_ligature_coverage =
+    + hb_iter (this + ligatureCoverage)
+    | hb_filter (glyphset)
+    | hb_map_retains_sorting (glyph_map)
+    ;
+
+    if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage))
+      return_trace (false);
+
+    out->ligatureArray.serialize_subset (c, ligatureArray, this,
+                                         hb_iter (this+ligatureCoverage), classCount, &klass_mapping);
+
+    return_trace (true);
+  }
+
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkMarkPos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkMarkPos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c0eee6d54cf8990d9989bdf8d9ef77f81360d2ae
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkMarkPos.hh
@@ -0,0 +1,36 @@
+#ifndef OT_LAYOUT_GPOS_MARKMARKPOS_HH
+#define OT_LAYOUT_GPOS_MARKMARKPOS_HH
+
+#include "MarkMarkPosFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct MarkMarkPos
+{
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  MarkMarkPosFormat1    format1;
+  } u;
+
+  public:
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKMARKPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c48a74f77389b02283abe75afd63401aa257f259
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh
@@ -0,0 +1,227 @@
+#ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
+#define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
+
+#include "MarkMarkPosFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+typedef AnchorMatrix Mark2Array;        /* mark2-major--
+                                         * in order of Mark2Coverage Index--,
+                                         * mark1-minor--
+                                         * ordered by class--zero-based. */
+
+struct MarkMarkPosFormat1
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  Offset16To<Coverage>
+                mark1Coverage;          /* Offset to Combining Mark1 Coverage
+                                         * table--from beginning of MarkMarkPos
+                                         * subtable */
+  Offset16To<Coverage>
+                mark2Coverage;          /* Offset to Combining Mark2 Coverage
+                                         * table--from beginning of MarkMarkPos
+                                         * subtable */
+  HBUINT16      classCount;             /* Number of defined mark classes */
+  Offset16To<MarkArray>
+                mark1Array;             /* Offset to Mark1Array table--from
+                                         * beginning of MarkMarkPos subtable */
+  Offset16To<Mark2Array>
+                mark2Array;             /* Offset to Mark2Array table--from
+                                         * beginning of MarkMarkPos subtable */
+  public:
+  DEFINE_SIZE_STATIC (12);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  mark1Coverage.sanitize (c, this) &&
+                  mark2Coverage.sanitize (c, this) &&
+                  mark1Array.sanitize (c, this) &&
+                  mark2Array.sanitize (c, this, (unsigned int) classCount));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    return (this+mark1Coverage).intersects (glyphs) &&
+           (this+mark2Coverage).intersects (glyphs);
+  }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    + hb_zip (this+mark1Coverage, this+mark1Array)
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); })
+    ;
+
+    hb_map_t klass_mapping;
+    Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping);
+
+    unsigned mark2_count = (this+mark2Array).rows;
+    auto mark2_iter =
+    + hb_zip (this+mark2Coverage, hb_range (mark2_count))
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    ;
+
+    hb_sorted_vector_t<unsigned> mark2_indexes;
+    for (const unsigned row : mark2_iter)
+    {
+      + hb_range ((unsigned) classCount)
+      | hb_filter (klass_mapping)
+      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
+      | hb_sink (mark2_indexes)
+      ;
+    }
+    (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ());
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return;
+    if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return;
+  }
+
+  const Coverage &get_coverage () const { return this+mark1Coverage; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int mark1_index = (this+mark1Coverage).get_coverage  (buffer->cur().codepoint);
+    if (likely (mark1_index == NOT_COVERED)) return_trace (false);
+
+    /* now we search backwards for a suitable mark glyph until a non-mark glyph */
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
+    skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
+    unsigned unsafe_from;
+    if (!skippy_iter.prev (&unsafe_from))
+    {
+      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]))
+    {
+      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    unsigned int j = skippy_iter.idx;
+
+    unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur());
+    unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]);
+    unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur());
+    unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]);
+
+    if (likely (id1 == id2))
+    {
+      if (id1 == 0) /* Marks belonging to the same base. */
+        goto good;
+      else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
+        goto good;
+    }
+    else
+    {
+      /* If ligature ids don't match, it may be the case that one of the marks
+       * itself is a ligature.  In which case match. */
+      if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
+        goto good;
+    }
+
+    /* Didn't match. */
+    buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+    return_trace (false);
+
+    good:
+    unsigned int mark2_index = (this+mark2Coverage).get_coverage  (buffer->info[j].codepoint);
+    if (mark2_index == NOT_COVERED)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    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);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    hb_map_t klass_mapping;
+    Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping);
+
+    if (!klass_mapping.get_population ()) return_trace (false);
+    out->classCount = klass_mapping.get_population ();
+
+    auto mark1_iter =
+    + hb_zip (this+mark1Coverage, this+mark1Array)
+    | hb_filter (glyphset, hb_first)
+    ;
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + mark1_iter
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
+      return_trace (false);
+
+    out->mark1Array.serialize_subset (c, mark1Array, this,
+                                      (this+mark1Coverage).iter (),
+                                      &klass_mapping);
+
+    unsigned mark2count = (this+mark2Array).rows;
+    auto mark2_iter =
+    + hb_zip (this+mark2Coverage, hb_range (mark2count))
+    | hb_filter (glyphset, hb_first)
+    ;
+
+    new_coverage.reset ();
+    + mark2_iter
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
+      return_trace (false);
+
+    hb_sorted_vector_t<unsigned> mark2_indexes;
+    for (const unsigned row : + mark2_iter
+                              | hb_map (hb_second))
+    {
+      + hb_range ((unsigned) classCount)
+      | hb_filter (klass_mapping)
+      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
+      | hb_sink (mark2_indexes)
+      ;
+    }
+
+    out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ());
+
+    return_trace (true);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkRecord.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkRecord.hh
new file mode 100644
index 0000000000000000000000000000000000000000..7a514453aeeed5888fdc6962cbbfed6356147a34
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/MarkRecord.hh
@@ -0,0 +1,52 @@
+#ifndef OT_LAYOUT_GPOS_MARKRECORD_HH
+#define OT_LAYOUT_GPOS_MARKRECORD_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct MarkRecord
+{
+  friend struct MarkArray;
+
+  protected:
+  HBUINT16      klass;                  /* Class defined for this mark */
+  Offset16To<Anchor>
+                markAnchor;             /* Offset to Anchor table--from
+                                         * beginning of MarkArray table */
+  public:
+  DEFINE_SIZE_STATIC (4);
+
+  unsigned get_class () const { return (unsigned) klass; }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && markAnchor.sanitize (c, base));
+  }
+
+  MarkRecord *subset (hb_subset_context_t    *c,
+                      const void             *src_base,
+                      const hb_map_t         *klass_mapping) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->klass = klass_mapping->get (klass);
+    out->markAnchor.serialize_subset (c, markAnchor, src_base);
+    return_trace (out);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const void *src_base) const
+  {
+    (src_base+markAnchor).collect_variation_indices (c);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_MARKRECORD_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8479178d384a52ebc24e5152a4e2a2de6871169d
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPos.hh
@@ -0,0 +1,38 @@
+#ifndef OT_LAYOUT_GPOS_PAIRPOS_HH
+#define OT_LAYOUT_GPOS_PAIRPOS_HH
+
+#include "PairPosFormat1.hh"
+#include "PairPosFormat2.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct PairPos
+{
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  PairPosFormat1        format1;
+  PairPosFormat2        format2;
+  } u;
+
+  public:
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+    case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+};
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_PAIRPOS_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..35a2db2d45eb54335e9f0c452438c9dc08cb5c91
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat1.hh
@@ -0,0 +1,420 @@
+#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
+#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct PairValueRecord
+{
+  friend struct PairSet;
+
+  int cmp (hb_codepoint_t k) const
+  { return secondGlyph.cmp (k); }
+
+  struct context_t
+  {
+    const void          *base;
+    const ValueFormat   *valueFormats;
+    const ValueFormat   *newFormats;
+    unsigned            len1; /* valueFormats[0].get_len() */
+    const hb_map_t      *glyph_map;
+    const hb_map_t      *layout_variation_idx_map;
+  };
+
+  bool subset (hb_subset_context_t *c,
+               context_t *closure) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *s = c->serializer;
+    auto *out = s->start_embed (*this);
+    if (unlikely (!s->extend_min (out))) return_trace (false);
+
+    out->secondGlyph = (*closure->glyph_map)[secondGlyph];
+
+    closure->valueFormats[0].copy_values (s,
+                                          closure->newFormats[0],
+                                          closure->base, &values[0],
+                                          closure->layout_variation_idx_map);
+    closure->valueFormats[1].copy_values (s,
+                                          closure->newFormats[1],
+                                          closure->base,
+                                          &values[closure->len1],
+                                          closure->layout_variation_idx_map);
+
+    return_trace (true);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const ValueFormat *valueFormats,
+                                  const void *base) const
+  {
+    unsigned record1_len = valueFormats[0].get_len ();
+    unsigned record2_len = valueFormats[1].get_len ();
+    const hb_array_t<const Value> values_array = values.as_array (record1_len + record2_len);
+
+    if (valueFormats[0].has_device ())
+      valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
+
+    if (valueFormats[1].has_device ())
+      valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
+  }
+
+  bool intersects (const hb_set_t& glyphset) const
+  {
+    return glyphset.has(secondGlyph);
+  }
+
+  const Value* get_values_1 () const
+  {
+    return &values[0];
+  }
+
+  const Value* get_values_2 (ValueFormat format1) const
+  {
+    return &values[format1.get_len ()];
+  }
+
+  protected:
+  HBGlyphID16   secondGlyph;            /* GlyphID of second glyph in the
+                                         * pair--first glyph is listed in the
+                                         * Coverage table */
+  ValueRecord   values;                 /* Positioning data for the first glyph
+                                         * followed by for second glyph */
+  public:
+  DEFINE_SIZE_ARRAY (2, values);
+};
+
+struct PairSet
+{
+  friend struct PairPosFormat1;
+
+  bool intersects (const hb_set_t *glyphs,
+                   const ValueFormat *valueFormats) const
+  {
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (glyphs->has (record->secondGlyph))
+        return true;
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+    return false;
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c,
+                       const ValueFormat *valueFormats) const
+  {
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    c->input->add_array (&record->secondGlyph, len, record_size);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const ValueFormat *valueFormats) const
+  {
+    unsigned len1 = valueFormats[0].get_len ();
+    unsigned len2 = valueFormats[1].get_len ();
+    unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned count = len;
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (c->glyph_set->has (record->secondGlyph))
+      { record->collect_variation_indices (c, valueFormats, this); }
+
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+  }
+
+  bool apply (hb_ot_apply_context_t *c,
+              const ValueFormat *valueFormats,
+              unsigned int pos) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
+                                                &firstPairValueRecord,
+                                                len,
+                                                record_size);
+    if (record)
+    {
+      bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
+      bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
+      if (applied_first || applied_second)
+        buffer->unsafe_to_break (buffer->idx, pos + 1);
+      if (len2)
+        pos++;
+      buffer->idx = pos;
+      return_trace (true);
+    }
+    buffer->unsafe_to_concat (buffer->idx, pos + 1);
+    return_trace (false);
+  }
+
+  bool subset (hb_subset_context_t *c,
+               const ValueFormat valueFormats[2],
+               const ValueFormat newFormats[2]) const
+  {
+    TRACE_SUBSET (this);
+    auto snap = c->serializer->snapshot ();
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->len = 0;
+
+    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 ();
+    unsigned len2 = valueFormats[1].get_len ();
+    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
+
+    PairValueRecord::context_t context =
+    {
+      this,
+      valueFormats,
+      newFormats,
+      len1,
+      &glyph_map,
+      c->plan->layout_variation_idx_map
+    };
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned count = len, num = 0;
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (glyphset.has (record->secondGlyph)
+         && record->subset (c, &context)) num++;
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+
+    out->len = num;
+    if (!num) c->serializer->revert (snap);
+    return_trace (num);
+  }
+
+  struct sanitize_closure_t
+  {
+    const ValueFormat *valueFormats;
+    unsigned int len1; /* valueFormats[0].get_len() */
+    unsigned int stride; /* 1 + len1 + len2 */
+  };
+
+  bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
+  {
+    TRACE_SANITIZE (this);
+    if (!(c->check_struct (this)
+       && c->check_range (&firstPairValueRecord,
+                          len,
+                          HBUINT16::static_size,
+                          closure->stride))) return_trace (false);
+
+    unsigned int count = len;
+    const PairValueRecord *record = &firstPairValueRecord;
+    return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
+                  closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
+  }
+
+  protected:
+  HBUINT16              len;    /* Number of PairValueRecords */
+  PairValueRecord       firstPairValueRecord;
+                                /* Array of PairValueRecords--ordered
+                                 * by GlyphID of the second glyph */
+  public:
+  DEFINE_SIZE_MIN (2);
+};
+
+struct PairPosFormat1
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  Offset16To<Coverage>
+                coverage;               /* Offset to Coverage table--from
+                                         * beginning of subtable */
+  ValueFormat   valueFormat[2];         /* [0] Defines the types of data in
+                                         * ValueRecord1--for the first glyph
+                                         * in the pair--may be zero (0) */
+                                        /* [1] Defines the types of data in
+                                         * ValueRecord2--for the second glyph
+                                         * in the pair--may be zero (0) */
+  Array16OfOffset16To<PairSet>
+                pairSet;                /* Array of PairSet tables
+                                         * ordered by Coverage Index */
+  public:
+  DEFINE_SIZE_ARRAY (10, pairSet);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    if (!c->check_struct (this)) return_trace (false);
+
+    unsigned int len1 = valueFormat[0].get_len ();
+    unsigned int len2 = valueFormat[1].get_len ();
+    PairSet::sanitize_closure_t closure =
+    {
+      valueFormat,
+      len1,
+      1 + len1 + len2
+    };
+
+    return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
+  }
+
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    return
+    + hb_zip (this+coverage, pairSet)
+    | hb_filter (*glyphs, hb_first)
+    | hb_map (hb_second)
+    | hb_map ([glyphs, this] (const Offset16To<PairSet> &_)
+              { return (this+_).intersects (glyphs, valueFormat); })
+    | hb_any
+    ;
+  }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return;
+
+    auto it =
+    + hb_zip (this+coverage, pairSet)
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    ;
+
+    if (!it) return;
+    + it
+    | hb_map (hb_add (this))
+    | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); })
+    ;
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+    unsigned int count = pairSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+pairSet[i]).collect_glyphs (c, valueFormat);
+  }
+
+  const Coverage &get_coverage () const { return this+coverage; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return_trace (false);
+
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
+    unsigned unsafe_to;
+    if (!skippy_iter.next (&unsafe_to))
+    {
+      buffer->unsafe_to_concat (buffer->idx, unsafe_to);
+      return_trace (false);
+    }
+
+    return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+
+    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);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+    out->valueFormat[0] = valueFormat[0];
+    out->valueFormat[1] = valueFormat[1];
+    if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+    {
+      hb_pair_t<unsigned, unsigned> newFormats = compute_effective_value_formats (glyphset);
+      out->valueFormat[0] = newFormats.first;
+      out->valueFormat[1] = newFormats.second;
+    }
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+
+    + hb_zip (this+coverage, pairSet)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter ([this, c, out] (const Offset16To<PairSet>& _)
+                 {
+                   auto snap = c->serializer->snapshot ();
+                   auto *o = out->pairSet.serialize_append (c->serializer);
+                   if (unlikely (!o)) return false;
+                   bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat);
+                   if (!ret)
+                   {
+                     out->pairSet.pop ();
+                     c->serializer->revert (snap);
+                   }
+                   return ret;
+                 },
+                 hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+
+    return_trace (bool (new_coverage));
+  }
+
+
+  hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset) const
+  {
+    unsigned len1 = valueFormat[0].get_len ();
+    unsigned len2 = valueFormat[1].get_len ();
+    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
+
+    unsigned format1 = 0;
+    unsigned format2 = 0;
+    for (const Offset16To<PairSet>& _ :
+             + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second))
+    {
+      const PairSet& set = (this + _);
+      const PairValueRecord *record = &set.firstPairValueRecord;
+
+      for (unsigned i = 0; i < set.len; i++)
+      {
+        if (record->intersects (glyphset))
+        {
+          format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ());
+          format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]));
+        }
+        record = &StructAtOffset<const PairValueRecord> (record, record_size);
+      }
+    }
+
+    return hb_pair (format1, format2);
+  }
+};
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat2.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat2.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3f5f9959c4675f56e3637abdf97f635c19f467c8
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat2.hh
@@ -0,0 +1,314 @@
+#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH
+#define OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH
+
+#include "ValueFormat.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct PairPosFormat2
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 2 */
+  Offset16To<Coverage>
+                coverage;               /* Offset to Coverage table--from
+                                         * beginning of subtable */
+  ValueFormat   valueFormat1;           /* ValueRecord definition--for the
+                                         * first glyph of the pair--may be zero
+                                         * (0) */
+  ValueFormat   valueFormat2;           /* ValueRecord definition--for the
+                                         * second glyph of the pair--may be
+                                         * zero (0) */
+  Offset16To<ClassDef>
+                classDef1;              /* Offset to ClassDef table--from
+                                         * beginning of PairPos subtable--for
+                                         * the first glyph of the pair */
+  Offset16To<ClassDef>
+                classDef2;              /* Offset to ClassDef table--from
+                                         * beginning of PairPos subtable--for
+                                         * the second glyph of the pair */
+  HBUINT16      class1Count;            /* Number of classes in ClassDef1
+                                         * table--includes Class0 */
+  HBUINT16      class2Count;            /* Number of classes in ClassDef2
+                                         * table--includes Class0 */
+  ValueRecord   values;                 /* Matrix of value pairs:
+                                         * class1-major, class2-minor,
+                                         * Each entry has value1 and value2 */
+  public:
+  DEFINE_SIZE_ARRAY (16, values);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!(c->check_struct (this)
+       && coverage.sanitize (c, this)
+       && classDef1.sanitize (c, this)
+       && classDef2.sanitize (c, this))) return_trace (false);
+
+    unsigned int len1 = valueFormat1.get_len ();
+    unsigned int len2 = valueFormat2.get_len ();
+    unsigned int stride = len1 + len2;
+    unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
+    unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
+    return_trace (c->check_range ((const void *) values,
+                                  count,
+                                  record_size) &&
+                  valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
+                  valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    return (this+coverage).intersects (glyphs) &&
+           (this+classDef2).intersects (glyphs);
+  }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    if (!intersects (c->glyph_set)) return;
+    if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return;
+
+    hb_set_t klass1_glyphs, klass2_glyphs;
+    if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return;
+    if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return;
+
+    hb_set_t class1_set, class2_set;
+    for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage))
+    {
+      if (!klass1_glyphs.has (cp)) class1_set.add (0);
+      else
+      {
+        unsigned klass1 = (this+classDef1).get (cp);
+        class1_set.add (klass1);
+      }
+    }
+
+    class2_set.add (0);
+    for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs))
+    {
+      unsigned klass2 = (this+classDef2).get (cp);
+      class2_set.add (klass2);
+    }
+
+    if (class1_set.is_empty ()
+        || class2_set.is_empty ()
+        || (class2_set.get_population() == 1 && class2_set.has(0)))
+      return;
+
+    unsigned len1 = valueFormat1.get_len ();
+    unsigned len2 = valueFormat2.get_len ();
+    const hb_array_t<const Value> values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2));
+    for (const unsigned class1_idx : class1_set.iter ())
+    {
+      for (const unsigned class2_idx : class2_set.iter ())
+      {
+        unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
+        if (valueFormat1.has_device ())
+          valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1));
+
+        if (valueFormat2.has_device ())
+          valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2));
+      }
+    }
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+    if (unlikely (!(this+classDef2).collect_coverage (c->input))) return;
+  }
+
+  const Coverage &get_coverage () const { return this+coverage; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return_trace (false);
+
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
+    unsigned unsafe_to;
+    if (!skippy_iter.next (&unsafe_to))
+    {
+      buffer->unsafe_to_concat (buffer->idx, unsafe_to);
+      return_trace (false);
+    }
+
+    unsigned int len1 = valueFormat1.get_len ();
+    unsigned int len2 = valueFormat2.get_len ();
+    unsigned int record_len = len1 + len2;
+
+    unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
+    unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
+    if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
+    {
+      buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
+      return_trace (false);
+    }
+
+    const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
+
+    bool applied_first = false, applied_second = false;
+
+
+    /* Isolate simple kerning and apply it half to each side.
+     * Results in better cursor positinoing / underline drawing.
+     *
+     * Disabled, because causes issues... :-(
+     * https://github.com/harfbuzz/harfbuzz/issues/3408
+     * https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978
+     */
+#ifndef HB_SPLIT_KERN
+    if (0)
+#endif
+    {
+      if (!len2)
+      {
+        const hb_direction_t dir = buffer->props.direction;
+        const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir);
+        const bool backward = HB_DIRECTION_IS_BACKWARD (dir);
+        unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance;
+        if (backward)
+          mask |= mask >> 2; /* Add eg. xPlacement in RTL. */
+        /* Add Devices. */
+        mask |= mask << 4;
+
+        if (valueFormat1 & ~mask)
+          goto bail;
+
+        /* Is simple kern. Apply value on an empty position slot,
+         * then split it between sides. */
+
+        hb_glyph_position_t pos{};
+        if (valueFormat1.apply_value (c, this, v, pos))
+        {
+          hb_position_t *src  = &pos.x_advance;
+          hb_position_t *dst1 = &buffer->cur_pos().x_advance;
+          hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance;
+          unsigned i = horizontal ? 0 : 1;
+
+          hb_position_t kern  = src[i];
+          hb_position_t kern1 = kern >> 1;
+          hb_position_t kern2 = kern - kern1;
+
+          if (!backward)
+          {
+            dst1[i] += kern1;
+            dst2[i] += kern2;
+            dst2[i + 2] += kern2;
+          }
+          else
+          {
+            dst1[i] += kern1;
+            dst1[i + 2] += src[i + 2] - kern2;
+            dst2[i] += kern2;
+          }
+
+          applied_first = applied_second = kern != 0;
+          goto success;
+        }
+        goto boring;
+      }
+    }
+    bail:
+
+
+    applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos());
+    applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
+
+    success:
+    if (applied_first || applied_second)
+      buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
+    else
+    boring:
+      buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
+
+
+    buffer->idx = skippy_iter.idx;
+    if (len2)
+      buffer->idx++;
+
+    return_trace (true);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    hb_map_t klass1_map;
+    out->classDef1.serialize_subset (c, classDef1, this, &klass1_map, true, true, &(this + coverage));
+    out->class1Count = klass1_map.get_population ();
+
+    hb_map_t klass2_map;
+    out->classDef2.serialize_subset (c, classDef2, this, &klass2_map, true, false);
+    out->class2Count = klass2_map.get_population ();
+
+    unsigned len1 = valueFormat1.get_len ();
+    unsigned len2 = valueFormat2.get_len ();
+
+    hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat1, valueFormat2);
+    if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+      newFormats = compute_effective_value_formats (klass1_map, klass2_map);
+
+    out->valueFormat1 = newFormats.first;
+    out->valueFormat2 = newFormats.second;
+
+    for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
+    {
+      for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
+      {
+        unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
+        valueFormat1.copy_values (c->serializer, newFormats.first, this, &values[idx], c->plan->layout_variation_idx_map);
+        valueFormat2.copy_values (c->serializer, newFormats.second, this, &values[idx + len1], c->plan->layout_variation_idx_map);
+      }
+    }
+
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto it =
+    + hb_iter (this+coverage)
+    | hb_filter (glyphset)
+    | hb_map_retains_sorting (glyph_map)
+    ;
+
+    out->coverage.serialize_serialize (c->serializer, it);
+    return_trace (out->class1Count && out->class2Count && bool (it));
+  }
+
+
+  hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_map_t& klass1_map,
+                                                                 const hb_map_t& klass2_map) const
+  {
+    unsigned len1 = valueFormat1.get_len ();
+    unsigned len2 = valueFormat2.get_len ();
+
+    unsigned format1 = 0;
+    unsigned format2 = 0;
+
+    for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
+    {
+      for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
+      {
+        unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
+        format1 = format1 | valueFormat1.get_effective_format (&values[idx]);
+        format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]);
+      }
+    }
+
+    return hb_pair (format1, format2);
+  }
+};
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PosLookup.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PosLookup.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c4e57bb543005323d3a753339efe5e2716b2cf42
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PosLookup.hh
@@ -0,0 +1,79 @@
+#ifndef OT_LAYOUT_GPOS_POSLOOKUP_HH
+#define OT_LAYOUT_GPOS_POSLOOKUP_HH
+
+#include "PosLookupSubTable.hh"
+#include "../../../hb-ot-layout-common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct PosLookup : Lookup
+{
+  using SubTable = PosLookupSubTable;
+
+  const SubTable& get_subtable (unsigned int i) const
+  { return Lookup::get_subtable<SubTable> (i); }
+
+  bool is_reverse () const
+  {
+    return false;
+  }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    return_trace (dispatch (c));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    hb_intersects_context_t c (glyphs);
+    return dispatch (&c);
+  }
+
+  hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
+  { return dispatch (c); }
+
+  hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
+  {
+    if (c->is_lookup_visited (this_index))
+      return hb_closure_lookups_context_t::default_return_value ();
+
+    c->set_lookup_visited (this_index);
+    if (!intersects (c->glyphs))
+    {
+      c->set_lookup_inactive (this_index);
+      return hb_closure_lookups_context_t::default_return_value ();
+    }
+
+    hb_closure_lookups_context_t::return_t ret = dispatch (c);
+    return ret;
+  }
+
+  template <typename set_t>
+  void collect_coverage (set_t *glyphs) const
+  {
+    hb_collect_coverage_context_t<set_t> c (glyphs);
+    dispatch (&c);
+  }
+
+  template <typename context_t>
+  static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
+
+  bool subset (hb_subset_context_t *c) const
+  { return Lookup::subset<SubTable> (c); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  { return Lookup::sanitize<SubTable> (c); }
+};
+
+}
+}
+}
+
+#endif  /* OT_LAYOUT_GPOS_POSLOOKUP_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PosLookupSubTable.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PosLookupSubTable.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c19fbc323ff81748c1972c330f71ffcf6ca929bd
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PosLookupSubTable.hh
@@ -0,0 +1,79 @@
+#ifndef OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH
+#define OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH
+
+#include "SinglePos.hh"
+#include "PairPos.hh"
+#include "CursivePos.hh"
+#include "MarkBasePos.hh"
+#include "MarkLigPos.hh"
+#include "MarkMarkPos.hh"
+#include "ContextPos.hh"
+#include "ChainContextPos.hh"
+#include "ExtensionPos.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct PosLookupSubTable
+{
+  friend struct ::OT::Lookup;
+  friend struct PosLookup;
+
+  enum Type {
+    Single              = 1,
+    Pair                = 2,
+    Cursive             = 3,
+    MarkBase            = 4,
+    MarkLig             = 5,
+    MarkMark            = 6,
+    Context             = 7,
+    ChainContext        = 8,
+    Extension           = 9
+  };
+
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
+  {
+    TRACE_DISPATCH (this, lookup_type);
+    switch (lookup_type) {
+    case Single:                return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
+    case Pair:                  return_trace (u.pair.dispatch (c, std::forward<Ts> (ds)...));
+    case Cursive:               return_trace (u.cursive.dispatch (c, std::forward<Ts> (ds)...));
+    case MarkBase:              return_trace (u.markBase.dispatch (c, std::forward<Ts> (ds)...));
+    case MarkLig:               return_trace (u.markLig.dispatch (c, std::forward<Ts> (ds)...));
+    case MarkMark:              return_trace (u.markMark.dispatch (c, std::forward<Ts> (ds)...));
+    case Context:               return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
+    case ChainContext:          return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
+    case Extension:             return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
+    default:                    return_trace (c->default_return_value ());
+    }
+  }
+
+  bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
+  {
+    hb_intersects_context_t c (glyphs);
+    return dispatch (&c, lookup_type);
+  }
+
+  protected:
+  union {
+  SinglePos             single;
+  PairPos               pair;
+  CursivePos            cursive;
+  MarkBasePos           markBase;
+  MarkLigPos            markLig;
+  MarkMarkPos           markMark;
+  ContextPos            context;
+  ChainContextPos       chainContext;
+  ExtensionPos          extension;
+  } u;
+  public:
+  DEFINE_SIZE_MIN (0);
+};
+
+}
+}
+}
+
+#endif  /* HB_OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePos.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePos.hh
new file mode 100644
index 0000000000000000000000000000000000000000..57e146befd62e2dc1e82545eeb32891625f868a7
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePos.hh
@@ -0,0 +1,98 @@
+#ifndef OT_LAYOUT_GPOS_SINGLEPOS_HH
+#define OT_LAYOUT_GPOS_SINGLEPOS_HH
+
+#include "SinglePosFormat1.hh"
+#include "SinglePosFormat2.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct SinglePos
+{
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  SinglePosFormat1      format1;
+  SinglePosFormat2      format2;
+  } u;
+
+  public:
+  template<typename Iterator,
+           hb_requires (hb_is_iterator (Iterator))>
+  unsigned get_format (Iterator glyph_val_iter_pairs)
+  {
+    hb_array_t<const Value> first_val_iter = hb_second (*glyph_val_iter_pairs);
+
+    for (const auto iter : glyph_val_iter_pairs)
+      for (const auto _ : hb_zip (iter.second, first_val_iter))
+        if (_.first != _.second)
+          return 2;
+
+    return 1;
+  }
+
+  template<typename Iterator,
+      typename SrcLookup,
+      hb_requires (hb_is_iterator (Iterator))>
+  void serialize (hb_serialize_context_t *c,
+                  const SrcLookup* src,
+                  Iterator glyph_val_iter_pairs,
+                  const hb_map_t *layout_variation_idx_map)
+  {
+    if (unlikely (!c->extend_min (u.format))) return;
+    unsigned format = 2;
+    ValueFormat new_format = src->get_value_format ();
+
+    if (glyph_val_iter_pairs)
+    {
+      format = get_format (glyph_val_iter_pairs);
+      new_format = src->get_value_format ().get_effective_format (+ glyph_val_iter_pairs | hb_map (hb_second));
+    }
+
+    u.format = format;
+    switch (u.format) {
+    case 1: u.format1.serialize (c,
+                                 src,
+                                 glyph_val_iter_pairs,
+                                 new_format,
+                                 layout_variation_idx_map);
+      return;
+    case 2: u.format2.serialize (c,
+                                 src,
+                                 glyph_val_iter_pairs,
+                                 new_format,
+                                 layout_variation_idx_map);
+      return;
+    default:return;
+    }
+  }
+
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+    case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+};
+
+
+template<typename Iterator, typename SrcLookup>
+static void
+SinglePos_serialize (hb_serialize_context_t *c,
+                     const SrcLookup *src,
+                     Iterator it,
+                     const hb_map_t *layout_variation_idx_map)
+{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_map); }
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8b7840ed0ebfd039a72f1c8fab9e9e4855068155
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat1.hh
@@ -0,0 +1,124 @@
+#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH
+#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH
+
+#include "Common.hh"
+#include "ValueFormat.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct SinglePosFormat1
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  Offset16To<Coverage>
+                coverage;               /* Offset to Coverage table--from
+                                         * beginning of subtable */
+  ValueFormat   valueFormat;            /* Defines the types of data in the
+                                         * ValueRecord */
+  ValueRecord   values;                 /* Defines positioning
+                                         * value(s)--applied to all glyphs in
+                                         * the Coverage table */
+  public:
+  DEFINE_SIZE_ARRAY (6, values);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  coverage.sanitize (c, this) &&
+                  valueFormat.sanitize_value (c, this, values));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    if (!valueFormat.has_device ()) return;
+
+    auto it =
+    + hb_iter (this+coverage)
+    | hb_filter (c->glyph_set)
+    ;
+
+    if (!it) return;
+    valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ()));
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
+
+  const Coverage &get_coverage () const { return this+coverage; }
+
+  ValueFormat get_value_format () const { return valueFormat; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return_trace (false);
+
+    valueFormat.apply_value (c, this, values, buffer->cur_pos());
+
+    buffer->idx++;
+    return_trace (true);
+  }
+
+  template<typename Iterator,
+      typename SrcLookup,
+      hb_requires (hb_is_iterator (Iterator))>
+  void serialize (hb_serialize_context_t *c,
+                  const SrcLookup *src,
+                  Iterator it,
+                  ValueFormat newFormat,
+                  const hb_map_t *layout_variation_idx_map)
+  {
+    if (unlikely (!c->extend_min (this))) return;
+    if (unlikely (!c->check_assign (valueFormat,
+                                    newFormat,
+                                    HB_SERIALIZE_ERROR_INT_OVERFLOW))) return;
+
+    for (const hb_array_t<const Value>& _ : + it | hb_map (hb_second))
+    {
+      src->get_value_format ().copy_values (c, newFormat, src,  &_, layout_variation_idx_map);
+      // Only serialize the first entry in the iterator, the rest are assumed to
+      // be the same.
+      break;
+    }
+
+    auto glyphs =
+    + it
+    | hb_map_retains_sorting (hb_first)
+    ;
+
+    coverage.serialize_serialize (c, glyphs);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto it =
+    + hb_iter (this+coverage)
+    | hb_filter (glyphset)
+    | hb_map_retains_sorting (glyph_map)
+    | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
+    ;
+
+    bool ret = bool (it);
+    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
+    return_trace (ret);
+  }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat2.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat2.hh
new file mode 100644
index 0000000000000000000000000000000000000000..0d038b44220e1f73f1c2336b0fee6d714cc350a7
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat2.hh
@@ -0,0 +1,140 @@
+#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH
+#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH
+
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+struct SinglePosFormat2
+{
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 2 */
+  Offset16To<Coverage>
+                coverage;               /* Offset to Coverage table--from
+                                         * beginning of subtable */
+  ValueFormat   valueFormat;            /* Defines the types of data in the
+                                         * ValueRecord */
+  HBUINT16      valueCount;             /* Number of ValueRecords */
+  ValueRecord   values;                 /* Array of ValueRecords--positioning
+                                         * values applied to glyphs */
+  public:
+  DEFINE_SIZE_ARRAY (8, values);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  coverage.sanitize (c, this) &&
+                  valueFormat.sanitize_values (c, this, values, valueCount));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    if (!valueFormat.has_device ()) return;
+
+    auto it =
+    + hb_zip (this+coverage, hb_range ((unsigned) valueCount))
+    | hb_filter (c->glyph_set, hb_first)
+    ;
+
+    if (!it) return;
+
+    unsigned sub_length = valueFormat.get_len ();
+    const hb_array_t<const Value> values_array = values.as_array (valueCount * sub_length);
+
+    for (unsigned i : + it
+                      | hb_map (hb_second))
+      valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length));
+
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
+
+  const Coverage &get_coverage () const { return this+coverage; }
+
+  ValueFormat get_value_format () const { return valueFormat; }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return_trace (false);
+
+    if (likely (index >= valueCount)) return_trace (false);
+
+    valueFormat.apply_value (c, this,
+                             &values[index * valueFormat.get_len ()],
+                             buffer->cur_pos());
+
+    buffer->idx++;
+    return_trace (true);
+  }
+
+  template<typename Iterator,
+      typename SrcLookup,
+      hb_requires (hb_is_iterator (Iterator))>
+  void serialize (hb_serialize_context_t *c,
+                  const SrcLookup *src,
+                  Iterator it,
+                  ValueFormat newFormat,
+                  const hb_map_t *layout_variation_idx_map)
+  {
+    auto out = c->extend_min (this);
+    if (unlikely (!out)) return;
+    if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return;
+    if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return;
+
+    + it
+    | hb_map (hb_second)
+    | hb_apply ([&] (hb_array_t<const Value> _)
+    { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); })
+    ;
+
+    auto glyphs =
+    + it
+    | hb_map_retains_sorting (hb_first)
+    ;
+
+    coverage.serialize_serialize (c, glyphs);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    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 ();
+    auto values_array = values.as_array (valueCount * sub_length);
+
+    auto it =
+    + hb_zip (this+coverage, hb_range ((unsigned) valueCount))
+    | hb_filter (glyphset, hb_first)
+    | hb_map_retains_sorting ([&] (const hb_pair_t<hb_codepoint_t, unsigned>& _)
+                              {
+                                return hb_pair (glyph_map[_.first],
+                                                values_array.sub_array (_.second * sub_length,
+                                                                        sub_length));
+                              })
+    ;
+
+    bool ret = bool (it);
+    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
+    return_trace (ret);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ValueFormat.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ValueFormat.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b29f287bcee9a97ec947625913394941ed912074
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/ValueFormat.hh
@@ -0,0 +1,329 @@
+#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
+#define OT_LAYOUT_GPOS_VALUEFORMAT_HH
+
+#include "../../../hb-ot-layout-gsubgpos.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+typedef HBUINT16 Value;
+
+typedef UnsizedArrayOf<Value> ValueRecord;
+
+struct ValueFormat : HBUINT16
+{
+  enum Flags {
+    xPlacement  = 0x0001u,      /* Includes horizontal adjustment for placement */
+    yPlacement  = 0x0002u,      /* Includes vertical adjustment for placement */
+    xAdvance    = 0x0004u,      /* Includes horizontal adjustment for advance */
+    yAdvance    = 0x0008u,      /* Includes vertical adjustment for advance */
+    xPlaDevice  = 0x0010u,      /* Includes horizontal Device table for placement */
+    yPlaDevice  = 0x0020u,      /* Includes vertical Device table for placement */
+    xAdvDevice  = 0x0040u,      /* Includes horizontal Device table for advance */
+    yAdvDevice  = 0x0080u,      /* Includes vertical Device table for advance */
+    ignored     = 0x0F00u,      /* Was used in TrueType Open for MM fonts */
+    reserved    = 0xF000u,      /* For future use */
+
+    devices     = 0x00F0u       /* Mask for having any Device table */
+  };
+
+/* All fields are options.  Only those available advance the value pointer. */
+#if 0
+  HBINT16               xPlacement;     /* Horizontal adjustment for
+                                         * placement--in design units */
+  HBINT16               yPlacement;     /* Vertical adjustment for
+                                         * placement--in design units */
+  HBINT16               xAdvance;       /* Horizontal adjustment for
+                                         * advance--in design units (only used
+                                         * for horizontal writing) */
+  HBINT16               yAdvance;       /* Vertical adjustment for advance--in
+                                         * design units (only used for vertical
+                                         * writing) */
+  Offset16To<Device>    xPlaDevice;     /* Offset to Device table for
+                                         * horizontal placement--measured from
+                                         * beginning of PosTable (may be NULL) */
+  Offset16To<Device>    yPlaDevice;     /* Offset to Device table for vertical
+                                         * placement--measured from beginning
+                                         * of PosTable (may be NULL) */
+  Offset16To<Device>    xAdvDevice;     /* Offset to Device table for
+                                         * horizontal advance--measured from
+                                         * beginning of PosTable (may be NULL) */
+  Offset16To<Device>    yAdvDevice;     /* Offset to Device table for vertical
+                                         * advance--measured from beginning of
+                                         * PosTable (may be NULL) */
+#endif
+
+  IntType& operator = (uint16_t i) { v = i; return *this; }
+
+  unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
+  unsigned int get_size () const { return get_len () * Value::static_size; }
+
+  bool apply_value (hb_ot_apply_context_t *c,
+                    const void            *base,
+                    const Value           *values,
+                    hb_glyph_position_t   &glyph_pos) const
+  {
+    bool ret = false;
+    unsigned int format = *this;
+    if (!format) return ret;
+
+    hb_font_t *font = c->font;
+    bool horizontal =
+#ifndef HB_NO_VERTICAL
+      HB_DIRECTION_IS_HORIZONTAL (c->direction)
+#else
+      true
+#endif
+      ;
+
+    if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
+    if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
+    if (format & xAdvance) {
+      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
+      values++;
+    }
+    /* y_advance values grow downward but font-space grows upward, hence negation */
+    if (format & yAdvance) {
+      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
+      values++;
+    }
+
+    if (!has_device ()) return ret;
+
+    bool use_x_device = font->x_ppem || font->num_coords;
+    bool use_y_device = font->y_ppem || font->num_coords;
+
+    if (!use_x_device && !use_y_device) return ret;
+
+    const VariationStore &store = c->var_store;
+    auto *cache = c->var_store_cache;
+
+    /* pixel -> fractional pixel */
+    if (format & xPlaDevice) {
+      if (use_x_device) glyph_pos.x_offset  += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
+      values++;
+    }
+    if (format & yPlaDevice) {
+      if (use_y_device) glyph_pos.y_offset  += (base + get_device (values, &ret)).get_y_delta (font, store, cache);
+      values++;
+    }
+    if (format & xAdvDevice) {
+      if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
+      values++;
+    }
+    if (format & yAdvDevice) {
+      /* y_advance values grow downward but font-space grows upward, hence negation */
+      if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store, cache);
+      values++;
+    }
+    return ret;
+  }
+
+  unsigned int get_effective_format (const Value *values) const
+  {
+    unsigned int format = *this;
+    for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
+      if (format & flag) should_drop (*values++, (Flags) flag, &format);
+    }
+
+    return format;
+  }
+
+  template<typename Iterator,
+      hb_requires (hb_is_iterator (Iterator))>
+  unsigned int get_effective_format (Iterator it) const {
+    unsigned int new_format = 0;
+
+    for (const hb_array_t<const Value>& values : it)
+      new_format = new_format | get_effective_format (&values);
+
+    return new_format;
+  }
+
+  void copy_values (hb_serialize_context_t *c,
+                    unsigned int new_format,
+                    const void *base,
+                    const Value *values,
+                    const hb_map_t *layout_variation_idx_map) const
+  {
+    unsigned int format = *this;
+    if (!format) return;
+
+    if (format & xPlacement) copy_value (c, new_format, xPlacement, *values++);
+    if (format & yPlacement) copy_value (c, new_format, yPlacement, *values++);
+    if (format & xAdvance)   copy_value (c, new_format, xAdvance, *values++);
+    if (format & yAdvance)   copy_value (c, new_format, yAdvance, *values++);
+
+    if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
+    if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
+    if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
+    if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
+  }
+
+  void copy_value (hb_serialize_context_t *c,
+                   unsigned int new_format,
+                   Flags flag,
+                   Value value) const
+  {
+    // Filter by new format.
+    if (!(new_format & flag)) return;
+    c->copy (value);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const void *base,
+                                  const hb_array_t<const Value>& values) const
+  {
+    unsigned format = *this;
+    unsigned i = 0;
+    if (format & xPlacement) i++;
+    if (format & yPlacement) i++;
+    if (format & xAdvance) i++;
+    if (format & yAdvance) i++;
+    if (format & xPlaDevice)
+    {
+      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      i++;
+    }
+
+    if (format & ValueFormat::yPlaDevice)
+    {
+      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      i++;
+    }
+
+    if (format & ValueFormat::xAdvDevice)
+    {
+
+      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      i++;
+    }
+
+    if (format & ValueFormat::yAdvDevice)
+    {
+
+      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      i++;
+    }
+  }
+
+  private:
+  bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  {
+    unsigned int format = *this;
+
+    if (format & xPlacement) values++;
+    if (format & yPlacement) values++;
+    if (format & xAdvance)   values++;
+    if (format & yAdvance)   values++;
+
+    if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
+    if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
+    if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
+    if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
+
+    return true;
+  }
+
+  static inline Offset16To<Device>& get_device (Value* value)
+  {
+    return *static_cast<Offset16To<Device> *> (value);
+  }
+  static inline const Offset16To<Device>& get_device (const Value* value, bool *worked=nullptr)
+  {
+    if (worked) *worked |= bool (*value);
+    return *static_cast<const Offset16To<Device> *> (value);
+  }
+
+  bool copy_device (hb_serialize_context_t *c, const void *base,
+                    const Value *src_value, const hb_map_t *layout_variation_idx_map) const
+  {
+    Value       *dst_value = c->copy (*src_value);
+
+    if (!dst_value) return false;
+    if (*dst_value == 0) return true;
+
+    *dst_value = 0;
+    c->push ();
+    if ((base + get_device (src_value)).copy (c, layout_variation_idx_map))
+    {
+      c->add_link (*dst_value, c->pop_pack ());
+      return true;
+    }
+    else
+    {
+      c->pop_discard ();
+      return false;
+    }
+  }
+
+  static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr)
+  {
+    if (worked) *worked |= bool (*value);
+    return *reinterpret_cast<const HBINT16 *> (value);
+  }
+
+  public:
+
+  bool has_device () const
+  {
+    unsigned int format = *this;
+    return (format & devices) != 0;
+  }
+
+  bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
+  }
+
+  bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    unsigned int len = get_len ();
+
+    if (!c->check_range (values, count, get_size ())) return_trace (false);
+
+    if (!has_device ()) return_trace (true);
+
+    for (unsigned int i = 0; i < count; i++) {
+      if (!sanitize_value_devices (c, base, values))
+        return_trace (false);
+      values += len;
+    }
+
+    return_trace (true);
+  }
+
+  /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
+  bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
+  {
+    TRACE_SANITIZE (this);
+
+    if (!has_device ()) return_trace (true);
+
+    for (unsigned int i = 0; i < count; i++) {
+      if (!sanitize_value_devices (c, base, values))
+        return_trace (false);
+      values += stride;
+    }
+
+    return_trace (true);
+  }
+
+ private:
+
+  void should_drop (Value value, Flags flag, unsigned int* format) const
+  {
+    if (value) return;
+    *format = *format & ~flag;
+  }
+
+};
+
+}
+}
+}
+
+#endif  // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/GSUB.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/GSUB.hh
index ad153ce8d788c3f372ccde074f72ef20c298122e..3f0c4b2ad9acb5c1824dcfa11bbdd33aae2d95d0 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/GSUB.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/GSUB.hh
@@ -19,6 +19,8 @@ namespace GSUB {
 
 struct GSUB : GSUBGPOS
 {
+  using Lookup = SubstLookup;
+
   static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
 
   const SubstLookup& get_lookup (unsigned int i) const
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SubstLookup.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SubstLookup.hh
index 3419b5a734c1a34a0ab5ac147a3b75a99c2fad16..8fb3b550976a20d5c615e18de86537d6a407b6e3 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SubstLookup.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SubstLookup.hh
@@ -10,7 +10,7 @@ namespace GSUB {
 
 struct SubstLookup : Lookup
 {
-  typedef SubstLookupSubTable SubTable;
+  using SubTable = SubstLookupSubTable;
 
   bool sanitize (hb_sanitize_context_t *c) const
   { return Lookup::sanitize<SubTable> (c); }
@@ -73,8 +73,6 @@ struct SubstLookup : Lookup
       return hb_closure_lookups_context_t::default_return_value ();
     }
 
-    c->set_recurse_func (dispatch_closure_lookups_recurse_func);
-
     hb_closure_lookups_context_t::return_t ret = dispatch (c);
     return ret;
   }
@@ -100,8 +98,6 @@ struct SubstLookup : Lookup
       return dispatch (c);
   }
 
-  static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
-
   bool serialize_single (hb_serialize_context_t *c,
                          uint32_t lookup_props,
                          hb_sorted_array_t<const HBGlyphID16> glyphs,
@@ -206,8 +202,6 @@ struct SubstLookup : Lookup
     return ret;
   }
 
-  HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index);
-
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/CompositeGlyph.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/CompositeGlyph.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c145beaa49adb53d3f1167bdd399e5119b061845
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/CompositeGlyph.hh
@@ -0,0 +1,258 @@
+#ifndef OT_GLYF_COMPOSITEGLYPH_HH
+#define OT_GLYF_COMPOSITEGLYPH_HH
+
+
+#include "../../hb-open-type.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+struct CompositeGlyphRecord
+{
+  protected:
+  enum composite_glyph_flag_t
+  {
+    ARG_1_AND_2_ARE_WORDS	= 0x0001,
+    ARGS_ARE_XY_VALUES		= 0x0002,
+    ROUND_XY_TO_GRID		= 0x0004,
+    WE_HAVE_A_SCALE		= 0x0008,
+    MORE_COMPONENTS		= 0x0020,
+    WE_HAVE_AN_X_AND_Y_SCALE	= 0x0040,
+    WE_HAVE_A_TWO_BY_TWO	= 0x0080,
+    WE_HAVE_INSTRUCTIONS	= 0x0100,
+    USE_MY_METRICS		= 0x0200,
+    OVERLAP_COMPOUND		= 0x0400,
+    SCALED_COMPONENT_OFFSET	= 0x0800,
+    UNSCALED_COMPONENT_OFFSET	= 0x1000
+  };
+
+  public:
+  unsigned int get_size () const
+  {
+    unsigned int size = min_size;
+    /* arg1 and 2 are int16 */
+    if (flags & ARG_1_AND_2_ARE_WORDS) size += 4;
+    /* arg1 and 2 are int8 */
+    else size += 2;
+
+    /* One x 16 bit (scale) */
+    if (flags & WE_HAVE_A_SCALE) size += 2;
+    /* Two x 16 bit (xscale, yscale) */
+    else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4;
+    /* Four x 16 bit (xscale, scale01, scale10, yscale) */
+    else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8;
+
+    return size;
+  }
+
+  void drop_instructions_flag ()  { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; }
+  void set_overlaps_flag ()
+  {
+    flags = (uint16_t) flags | OVERLAP_COMPOUND;
+  }
+
+  bool has_instructions ()  const { return   flags & WE_HAVE_INSTRUCTIONS; }
+
+  bool has_more ()          const { return   flags & MORE_COMPONENTS; }
+  bool is_use_my_metrics () const { return   flags & USE_MY_METRICS; }
+  bool is_anchored ()       const { return !(flags & ARGS_ARE_XY_VALUES); }
+  void get_anchor_points (unsigned int &point1, unsigned int &point2) const
+  {
+    const HBUINT8 *p = &StructAfter<const HBUINT8> (glyphIndex);
+    if (flags & ARG_1_AND_2_ARE_WORDS)
+    {
+      point1 = ((const HBUINT16 *) p)[0];
+      point2 = ((const HBUINT16 *) p)[1];
+    }
+    else
+    {
+      point1 = p[0];
+      point2 = p[1];
+    }
+  }
+
+  void transform_points (contour_point_vector_t &points) const
+  {
+    float matrix[4];
+    contour_point_t trans;
+    if (get_transformation (matrix, trans))
+    {
+      if (scaled_offsets ())
+      {
+	points.translate (trans);
+	points.transform (matrix);
+      }
+      else
+      {
+	points.transform (matrix);
+	points.translate (trans);
+      }
+    }
+  }
+
+  protected:
+  bool scaled_offsets () const
+  { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; }
+
+  bool get_transformation (float (&matrix)[4], contour_point_t &trans) const
+  {
+    matrix[0] = matrix[3] = 1.f;
+    matrix[1] = matrix[2] = 0.f;
+
+    int tx, ty;
+    const HBINT8 *p = &StructAfter<const HBINT8> (glyphIndex);
+    if (flags & ARG_1_AND_2_ARE_WORDS)
+    {
+      tx = *(const HBINT16 *) p;
+      p += HBINT16::static_size;
+      ty = *(const HBINT16 *) p;
+      p += HBINT16::static_size;
+    }
+    else
+    {
+      tx = *p++;
+      ty = *p++;
+    }
+    if (is_anchored ()) tx = ty = 0;
+
+    trans.init ((float) tx, (float) ty);
+
+    {
+      const F2DOT14 *points = (const F2DOT14 *) p;
+      if (flags & WE_HAVE_A_SCALE)
+      {
+	matrix[0] = matrix[3] = points[0].to_float ();
+	return true;
+      }
+      else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
+      {
+	matrix[0] = points[0].to_float ();
+	matrix[3] = points[1].to_float ();
+	return true;
+      }
+      else if (flags & WE_HAVE_A_TWO_BY_TWO)
+      {
+	matrix[0] = points[0].to_float ();
+	matrix[1] = points[1].to_float ();
+	matrix[2] = points[2].to_float ();
+	matrix[3] = points[3].to_float ();
+	return true;
+      }
+    }
+    return tx || ty;
+  }
+
+  public:
+  HBUINT16	flags;
+  HBGlyphID16	glyphIndex;
+  public:
+  DEFINE_SIZE_MIN (4);
+};
+
+struct composite_iter_t : hb_iter_with_fallback_t<composite_iter_t, const CompositeGlyphRecord &>
+{
+  typedef const CompositeGlyphRecord *__item_t__;
+  composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) :
+      glyph (glyph_), current (nullptr), current_size (0)
+  {
+    set_current (current_);
+  }
+
+  composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {}
+
+  item_t __item__ () const { return *current; }
+  bool __more__ () const { return current; }
+  void __next__ ()
+  {
+    if (!current->has_more ()) { current = nullptr; return; }
+
+    set_current (&StructAtOffset<CompositeGlyphRecord> (current, current_size));
+  }
+  composite_iter_t __end__ () const { return composite_iter_t (); }
+  bool operator != (const composite_iter_t& o) const
+  { return current != o.current; }
+
+
+  void set_current (__item_t__ current_)
+  {
+    if (!glyph.check_range (current_, CompositeGlyphRecord::min_size))
+    {
+      current = nullptr;
+      current_size = 0;
+      return;
+    }
+    unsigned size = current_->get_size ();
+    if (!glyph.check_range (current_, size))
+    {
+      current = nullptr;
+      current_size = 0;
+      return;
+    }
+
+    current = current_;
+    current_size = size;
+  }
+
+  private:
+  hb_bytes_t glyph;
+  __item_t__ current;
+  unsigned current_size;
+};
+
+struct CompositeGlyph
+{
+  const GlyphHeader &header;
+  hb_bytes_t bytes;
+  CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
+    header (header_), bytes (bytes_) {}
+
+  composite_iter_t iter () const
+  { return composite_iter_t (bytes, &StructAfter<CompositeGlyphRecord, GlyphHeader> (header)); }
+
+  unsigned int instructions_length (hb_bytes_t bytes) const
+  {
+    unsigned int start = bytes.length;
+    unsigned int end = bytes.length;
+    const CompositeGlyphRecord *last = nullptr;
+    for (auto &item : iter ())
+      last = &item;
+    if (unlikely (!last)) return 0;
+
+    if (last->has_instructions ())
+      start = (char *) last - &bytes + last->get_size ();
+    if (unlikely (start > end)) return 0;
+    return end - start;
+  }
+
+  /* Trimming for composites not implemented.
+   * If removing hints it falls out of that. */
+  const hb_bytes_t trim_padding () const { return bytes; }
+
+  void drop_hints ()
+  {
+    for (const auto &_ : iter ())
+      const_cast<CompositeGlyphRecord &> (_).drop_instructions_flag ();
+  }
+
+  /* Chop instructions off the end */
+  void drop_hints_bytes (hb_bytes_t &dest_start) const
+  { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); }
+
+  void set_overlaps_flag ()
+  {
+    CompositeGlyphRecord& glyph_chain = const_cast<CompositeGlyphRecord &> (
+	StructAfter<CompositeGlyphRecord, GlyphHeader> (header));
+    if (!bytes.check_range(&glyph_chain, CompositeGlyphRecord::min_size))
+      return;
+    glyph_chain.set_overlaps_flag ();
+  }
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_COMPOSITEGLYPH_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/Glyph.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/Glyph.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2199d2c48b8c29adea2ba1a0ebe550707b076bf9
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/Glyph.hh
@@ -0,0 +1,233 @@
+#ifndef OT_GLYF_GLYPH_HH
+#define OT_GLYF_GLYPH_HH
+
+
+#include "../../hb-open-type.hh"
+
+#include "GlyphHeader.hh"
+#include "SimpleGlyph.hh"
+#include "CompositeGlyph.hh"
+
+
+namespace OT {
+
+struct glyf_accelerator_t;
+
+namespace glyf_impl {
+
+
+enum phantom_point_index_t
+{
+  PHANTOM_LEFT   = 0,
+  PHANTOM_RIGHT  = 1,
+  PHANTOM_TOP    = 2,
+  PHANTOM_BOTTOM = 3,
+  PHANTOM_COUNT  = 4
+};
+
+struct Glyph
+{
+  enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE };
+
+  public:
+  composite_iter_t get_composite_iterator () const
+  {
+    if (type != COMPOSITE) return composite_iter_t ();
+    return CompositeGlyph (*header, bytes).iter ();
+  }
+
+  const hb_bytes_t trim_padding () const
+  {
+    switch (type) {
+    case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
+    case SIMPLE:    return SimpleGlyph (*header, bytes).trim_padding ();
+    default:        return bytes;
+    }
+  }
+
+  void drop_hints ()
+  {
+    switch (type) {
+    case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
+    case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints (); return;
+    default:        return;
+    }
+  }
+
+  void set_overlaps_flag ()
+  {
+    switch (type) {
+    case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
+    case SIMPLE:    SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
+    default:        return;
+    }
+  }
+
+  void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
+  {
+    switch (type) {
+    case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
+    case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
+    default:        return;
+    }
+  }
+
+  /* Note: Recursively calls itself.
+   * all_points includes phantom points
+   */
+  template <typename accelerator_t>
+  bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
+		   contour_point_vector_t &all_points /* OUT */,
+		   bool phantom_only = false,
+		   unsigned int depth = 0) const
+  {
+    if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
+    contour_point_vector_t stack_points;
+    bool inplace = type == SIMPLE && all_points.length == 0;
+    /* Load into all_points if it's empty, as an optimization. */
+    contour_point_vector_t &points = inplace ? all_points : stack_points;
+
+    switch (type) {
+    case COMPOSITE:
+    {
+      /* pseudo component points for each component in composite glyph */
+      unsigned num_points = hb_len (CompositeGlyph (*header, bytes).iter ());
+      if (unlikely (!points.resize (num_points))) return false;
+      break;
+    }
+    case SIMPLE:
+      if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
+	return false;
+      break;
+    }
+
+    /* Init phantom points */
+    if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
+    hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
+    {
+      int h_delta = (int) header->xMin -
+		    glyf_accelerator.hmtx->get_side_bearing (gid);
+      int v_orig  = (int) header->yMax +
+#ifndef HB_NO_VERTICAL
+		    glyf_accelerator.vmtx->get_side_bearing (gid)
+#else
+		    0
+#endif
+		    ;
+      unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid);
+      unsigned v_adv =
+#ifndef HB_NO_VERTICAL
+		       glyf_accelerator.vmtx->get_advance (gid)
+#else
+		       - font->face->get_upem ()
+#endif
+		       ;
+      phantoms[PHANTOM_LEFT].x = h_delta;
+      phantoms[PHANTOM_RIGHT].x = h_adv + h_delta;
+      phantoms[PHANTOM_TOP].y = v_orig;
+      phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
+    }
+
+#ifndef HB_NO_VAR
+    glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ());
+#endif
+
+    switch (type) {
+    case SIMPLE:
+      if (!inplace)
+	all_points.extend (points.as_array ());
+      break;
+    case COMPOSITE:
+    {
+      contour_point_vector_t comp_points;
+      unsigned int comp_index = 0;
+      for (auto &item : get_composite_iterator ())
+      {
+        comp_points.reset ();
+	if (unlikely (!glyf_accelerator.glyph_for_gid (item.glyphIndex)
+				       .get_points (font, glyf_accelerator, comp_points,
+						    phantom_only, depth + 1)))
+	  return false;
+
+	/* Copy phantom points from component if USE_MY_METRICS flag set */
+	if (item.is_use_my_metrics ())
+	  for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
+	    phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
+
+	/* Apply component transformation & translation */
+	item.transform_points (comp_points);
+
+	/* Apply translation from gvar */
+	comp_points.translate (points[comp_index]);
+
+	if (item.is_anchored ())
+	{
+	  unsigned int p1, p2;
+	  item.get_anchor_points (p1, p2);
+	  if (likely (p1 < all_points.length && p2 < comp_points.length))
+	  {
+	    contour_point_t delta;
+	    delta.init (all_points[p1].x - comp_points[p2].x,
+			all_points[p1].y - comp_points[p2].y);
+
+	    comp_points.translate (delta);
+	  }
+	}
+
+	all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT));
+
+	comp_index++;
+      }
+
+      all_points.extend (phantoms);
+    } break;
+    default:
+      all_points.extend (phantoms);
+    }
+
+    if (depth == 0) /* Apply at top level */
+    {
+      /* Undocumented rasterizer behavior:
+       * Shift points horizontally by the updated left side bearing
+       */
+      contour_point_t delta;
+      delta.init (-phantoms[PHANTOM_LEFT].x, 0.f);
+      if (delta.x) all_points.translate (delta);
+    }
+
+    return !all_points.in_error ();
+  }
+
+  bool get_extents (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator,
+		    hb_glyph_extents_t *extents) const
+  {
+    if (type == EMPTY) return true; /* Empty glyph; zero extents. */
+    return header->get_extents (font, glyf_accelerator, gid, extents);
+  }
+
+  hb_bytes_t get_bytes () const { return bytes; }
+
+  Glyph (hb_bytes_t bytes_ = hb_bytes_t (),
+	 hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_),
+						      header (bytes.as<GlyphHeader> ()),
+						      gid (gid_)
+  {
+    int num_contours = header->numberOfContours;
+    if (unlikely (num_contours == 0)) type = EMPTY;
+    else if (num_contours > 0) type = SIMPLE;
+    else type = COMPOSITE; /* negative numbers */
+  }
+
+  protected:
+  hb_bytes_t bytes;
+  const GlyphHeader *header;
+  hb_codepoint_t gid;
+  unsigned type;
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_GLYPH_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/GlyphHeader.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/GlyphHeader.hh
new file mode 100644
index 0000000000000000000000000000000000000000..792bd5478f59e2bda40f4c72751f30993780e443
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/GlyphHeader.hh
@@ -0,0 +1,48 @@
+#ifndef OT_GLYF_GLYPHHEADER_HH
+#define OT_GLYF_GLYPHHEADER_HH
+
+
+#include "../../hb-open-type.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+struct GlyphHeader
+{
+  bool has_data () const { return numberOfContours; }
+
+  template <typename accelerator_t>
+  bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator,
+		    hb_codepoint_t gid, hb_glyph_extents_t *extents) const
+  {
+    /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */
+    /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
+    extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid));
+    extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax));
+    extents->width     = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax));
+    extents->height    = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax));
+
+    return true;
+  }
+
+  HBINT16	numberOfContours;
+		    /* If the number of contours is
+		     * greater than or equal to zero,
+		     * this is a simple glyph; if negative,
+		     * this is a composite glyph. */
+  FWORD	xMin;	/* Minimum x for coordinate data. */
+  FWORD	yMin;	/* Minimum y for coordinate data. */
+  FWORD	xMax;	/* Maximum x for coordinate data. */
+  FWORD	yMax;	/* Maximum y for coordinate data. */
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_GLYPHHEADER_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SimpleGlyph.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SimpleGlyph.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6df978cf13e18a2e8d235862ac4afa8ed5f2a09d
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SimpleGlyph.hh
@@ -0,0 +1,216 @@
+#ifndef OT_GLYF_SIMPLEGLYPH_HH
+#define OT_GLYF_SIMPLEGLYPH_HH
+
+
+#include "../../hb-open-type.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+struct SimpleGlyph
+{
+  enum simple_glyph_flag_t
+  {
+    FLAG_ON_CURVE       = 0x01,
+    FLAG_X_SHORT        = 0x02,
+    FLAG_Y_SHORT        = 0x04,
+    FLAG_REPEAT         = 0x08,
+    FLAG_X_SAME         = 0x10,
+    FLAG_Y_SAME         = 0x20,
+    FLAG_OVERLAP_SIMPLE = 0x40,
+    FLAG_RESERVED2      = 0x80
+  };
+
+  const GlyphHeader &header;
+  hb_bytes_t bytes;
+  SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
+    header (header_), bytes (bytes_) {}
+
+  unsigned int instruction_len_offset () const
+  { return GlyphHeader::static_size + 2 * header.numberOfContours; }
+
+  unsigned int length (unsigned int instruction_len) const
+  { return instruction_len_offset () + 2 + instruction_len; }
+
+  unsigned int instructions_length () const
+  {
+    unsigned int instruction_length_offset = instruction_len_offset ();
+    if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0;
+
+    const HBUINT16 &instructionLength = StructAtOffset<HBUINT16> (&bytes, instruction_length_offset);
+    /* Out of bounds of the current glyph */
+    if (unlikely (length (instructionLength) > bytes.length)) return 0;
+    return instructionLength;
+  }
+
+  const hb_bytes_t trim_padding () const
+  {
+    /* based on FontTools _g_l_y_f.py::trim */
+    const uint8_t *glyph = (uint8_t*) bytes.arrayZ;
+    const uint8_t *glyph_end = glyph + bytes.length;
+    /* simple glyph w/contours, possibly trimmable */
+    glyph += instruction_len_offset ();
+
+    if (unlikely (glyph + 2 >= glyph_end)) return hb_bytes_t ();
+    unsigned int num_coordinates = StructAtOffset<HBUINT16> (glyph - 2, 0) + 1;
+    unsigned int num_instructions = StructAtOffset<HBUINT16> (glyph, 0);
+
+    glyph += 2 + num_instructions;
+
+    unsigned int coord_bytes = 0;
+    unsigned int coords_with_flags = 0;
+    while (glyph < glyph_end)
+    {
+      uint8_t flag = *glyph;
+      glyph++;
+
+      unsigned int repeat = 1;
+      if (flag & FLAG_REPEAT)
+      {
+	if (unlikely (glyph >= glyph_end)) return hb_bytes_t ();
+	repeat = *glyph + 1;
+	glyph++;
+      }
+
+      unsigned int xBytes, yBytes;
+      xBytes = yBytes = 0;
+      if (flag & FLAG_X_SHORT) xBytes = 1;
+      else if ((flag & FLAG_X_SAME) == 0) xBytes = 2;
+
+      if (flag & FLAG_Y_SHORT) yBytes = 1;
+      else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2;
+
+      coord_bytes += (xBytes + yBytes) * repeat;
+      coords_with_flags += repeat;
+      if (coords_with_flags >= num_coordinates) break;
+    }
+
+    if (unlikely (coords_with_flags != num_coordinates)) return hb_bytes_t ();
+    return bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph));
+  }
+
+  /* zero instruction length */
+  void drop_hints ()
+  {
+    GlyphHeader &glyph_header = const_cast<GlyphHeader &> (header);
+    (HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0;
+  }
+
+  void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
+  {
+    unsigned int instructions_len = instructions_length ();
+    unsigned int glyph_length = length (instructions_len);
+    dest_start = bytes.sub_array (0, glyph_length - instructions_len);
+    dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length);
+  }
+
+  void set_overlaps_flag ()
+  {
+    if (unlikely (!header.numberOfContours)) return;
+
+    unsigned flags_offset = length (instructions_length ());
+    if (unlikely (flags_offset + 1 > bytes.length)) return;
+
+    HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset<HBUINT16> (&bytes, flags_offset);
+    first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE;
+  }
+
+  static bool read_flags (const HBUINT8 *&p /* IN/OUT */,
+			  contour_point_vector_t &points_ /* IN/OUT */,
+			  const HBUINT8 *end)
+  {
+    unsigned count = points_.length;
+    for (unsigned int i = 0; i < count;)
+    {
+      if (unlikely (p + 1 > end)) return false;
+      uint8_t flag = *p++;
+      points_.arrayZ[i++].flag = flag;
+      if (flag & FLAG_REPEAT)
+      {
+	if (unlikely (p + 1 > end)) return false;
+	unsigned int repeat_count = *p++;
+	unsigned stop = hb_min (i + repeat_count, count);
+	for (; i < stop;)
+	  points_.arrayZ[i++].flag = flag;
+      }
+    }
+    return true;
+  }
+
+  static bool read_points (const HBUINT8 *&p /* IN/OUT */,
+			   contour_point_vector_t &points_ /* IN/OUT */,
+			   const HBUINT8 *end,
+			   float contour_point_t::*m,
+			   const simple_glyph_flag_t short_flag,
+			   const simple_glyph_flag_t same_flag)
+  {
+    int v = 0;
+
+    unsigned count = points_.length;
+    for (unsigned i = 0; i < count; i++)
+    {
+      unsigned flag = points_[i].flag;
+      if (flag & short_flag)
+      {
+	if (unlikely (p + 1 > end)) return false;
+	if (flag & same_flag)
+	  v += *p++;
+	else
+	  v -= *p++;
+      }
+      else
+      {
+	if (!(flag & same_flag))
+	{
+	  if (unlikely (p + HBINT16::static_size > end)) return false;
+	  v += *(const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	}
+      }
+      points_.arrayZ[i].*m = v;
+    }
+    return true;
+  }
+
+  bool get_contour_points (contour_point_vector_t &points_ /* OUT */,
+			   bool phantom_only = false) const
+  {
+    const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header);
+    int num_contours = header.numberOfContours;
+    assert (num_contours);
+    /* One extra item at the end, for the instruction-count below. */
+    if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false;
+    unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
+
+    points_.alloc (num_points + 4); // Allocate for phantom points, to avoid a possible copy
+    if (!points_.resize (num_points)) return false;
+    if (phantom_only) return true;
+
+    for (int i = 0; i < num_contours; i++)
+      points_[endPtsOfContours[i]].is_end_point = true;
+
+    /* Skip instructions */
+    const HBUINT8 *p = &StructAtOffset<HBUINT8> (&endPtsOfContours[num_contours + 1],
+						 endPtsOfContours[num_contours]);
+
+    if (unlikely ((const char *) p < bytes.arrayZ)) return false; /* Unlikely overflow */
+    const HBUINT8 *end = (const HBUINT8 *) (bytes.arrayZ + bytes.length);
+    if (unlikely (p >= end)) return false;
+
+    /* Read x & y coordinates */
+    return read_flags (p, points_, end)
+        && read_points (p, points_, end, &contour_point_t::x,
+			FLAG_X_SHORT, FLAG_X_SAME)
+	&& read_points (p, points_, end, &contour_point_t::y,
+			FLAG_Y_SHORT, FLAG_Y_SAME);
+  }
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_SIMPLEGLYPH_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SubsetGlyph.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SubsetGlyph.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ebe437204750995dd48d8745acbf182b2d1d06e5
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SubsetGlyph.hh
@@ -0,0 +1,72 @@
+#ifndef OT_GLYF_SUBSETGLYPH_HH
+#define OT_GLYF_SUBSETGLYPH_HH
+
+
+#include "../../hb-open-type.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+struct SubsetGlyph
+{
+  hb_codepoint_t new_gid;
+  hb_codepoint_t old_gid;
+  Glyph source_glyph;
+  hb_bytes_t dest_start;  /* region of source_glyph to copy first */
+  hb_bytes_t dest_end;    /* region of source_glyph to copy second */
+
+  bool serialize (hb_serialize_context_t *c,
+		  bool use_short_loca,
+		  const hb_subset_plan_t *plan) const
+  {
+    TRACE_SERIALIZE (this);
+
+    hb_bytes_t dest_glyph = dest_start.copy (c);
+    dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length);
+    unsigned int pad_length = use_short_loca ? padding () : 0;
+    DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
+
+    HBUINT8 pad;
+    pad = 0;
+    while (pad_length > 0)
+    {
+      c->embed (pad);
+      pad_length--;
+    }
+
+    if (unlikely (!dest_glyph.length)) return_trace (true);
+
+    /* update components gids */
+    for (auto &_ : Glyph (dest_glyph).get_composite_iterator ())
+    {
+      hb_codepoint_t new_gid;
+      if (plan->new_gid_for_old_gid (_.glyphIndex, &new_gid))
+	const_cast<CompositeGlyphRecord &> (_).glyphIndex = new_gid;
+    }
+
+    if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+      Glyph (dest_glyph).drop_hints ();
+
+    if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG)
+      Glyph (dest_glyph).set_overlaps_flag ();
+
+    return_trace (true);
+  }
+
+  void drop_hints_bytes ()
+  { source_glyph.drop_hints_bytes (dest_start, dest_end); }
+
+  unsigned int      length () const { return dest_start.length + dest_end.length; }
+  /* pad to 2 to ensure 2-byte loca will be ok */
+  unsigned int     padding () const { return length () % 2; }
+  unsigned int padded_size () const { return length () + padding (); }
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_SUBSETGLYPH_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf-helpers.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf-helpers.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f51f7a81fcc618d21b8f8bff0f8fa09e25b4712e
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf-helpers.hh
@@ -0,0 +1,90 @@
+#ifndef OT_GLYF_GLYF_HELPERS_HH
+#define OT_GLYF_GLYF_HELPERS_HH
+
+
+#include "../../hb-open-type.hh"
+#include "../../hb-subset-plan.hh"
+
+#include "loca.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+template<typename IteratorIn, typename IteratorOut,
+	 hb_requires (hb_is_source_of (IteratorIn, unsigned int)),
+	 hb_requires (hb_is_sink_of (IteratorOut, unsigned))>
+static void
+_write_loca (IteratorIn it, bool short_offsets, IteratorOut dest)
+{
+  unsigned right_shift = short_offsets ? 1 : 0;
+  unsigned int offset = 0;
+  dest << 0;
+  + it
+  | hb_map ([=, &offset] (unsigned int padded_size)
+	    {
+	      offset += padded_size;
+	      DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset);
+	      return offset >> right_shift;
+	    })
+  | hb_sink (dest)
+  ;
+}
+
+static bool
+_add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
+{
+  hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<head> (plan->source);
+  hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
+  hb_blob_destroy (head_blob);
+
+  if (unlikely (!head_prime_blob))
+    return false;
+
+  head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
+  head_prime->indexToLocFormat = use_short_loca ? 0 : 1;
+  bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
+
+  hb_blob_destroy (head_prime_blob);
+  return success;
+}
+
+template<typename Iterator,
+	 hb_requires (hb_is_source_of (Iterator, unsigned int))>
+static bool
+_add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca)
+{
+  unsigned num_offsets = padded_offsets.len () + 1;
+  unsigned entry_size = use_short_loca ? 2 : 4;
+  char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets);
+
+  if (unlikely (!loca_prime_data)) return false;
+
+  DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d",
+	     entry_size, num_offsets, entry_size * num_offsets);
+
+  if (use_short_loca)
+    _write_loca (padded_offsets, true, hb_array ((HBUINT16 *) loca_prime_data, num_offsets));
+  else
+    _write_loca (padded_offsets, false, hb_array ((HBUINT32 *) loca_prime_data, num_offsets));
+
+  hb_blob_t *loca_blob = hb_blob_create (loca_prime_data,
+					 entry_size * num_offsets,
+					 HB_MEMORY_MODE_WRITABLE,
+					 loca_prime_data,
+					 hb_free);
+
+  bool result = plan->add_table (HB_OT_TAG_loca, loca_blob)
+	     && _add_head_and_set_loca_version (plan, use_short_loca);
+
+  hb_blob_destroy (loca_blob);
+  return result;
+}
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_GLYF_HELPERS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f74513cb968bcc4e34c0b73eca54333345163bb9
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh
@@ -0,0 +1,388 @@
+#ifndef OT_GLYF_GLYF_HH
+#define OT_GLYF_GLYF_HH
+
+
+#include "../../hb-open-type.hh"
+#include "../../hb-ot-head-table.hh"
+#include "../../hb-ot-hmtx-table.hh"
+#include "../../hb-ot-var-gvar-table.hh"
+#include "../../hb-draw.hh"
+
+#include "glyf-helpers.hh"
+#include "Glyph.hh"
+#include "SubsetGlyph.hh"
+#include "loca.hh"
+#include "path-builder.hh"
+
+
+namespace OT {
+
+
+/*
+ * glyf -- TrueType Glyph Data
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
+ */
+#define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
+
+
+struct glyf
+{
+  friend struct glyf_accelerator_t;
+
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
+
+  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
+  {
+    TRACE_SANITIZE (this);
+    /* Runtime checks as eager sanitizing each glyph is costy */
+    return_trace (true);
+  }
+
+  /* requires source of SubsetGlyph complains the identifier isn't declared */
+  template <typename Iterator>
+  bool serialize (hb_serialize_context_t *c,
+		  Iterator it,
+                  bool use_short_loca,
+		  const hb_subset_plan_t *plan)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned init_len = c->length ();
+    for (const auto &_ : it) _.serialize (c, use_short_loca, plan);
+
+    /* As a special case when all glyph in the font are empty, add a zero byte
+     * to the table, so that OTS doesn’t reject it, and to make the table work
+     * on Windows as well.
+     * See https://github.com/khaledhosny/ots/issues/52 */
+    if (init_len == c->length ())
+    {
+      HBUINT8 empty_byte;
+      empty_byte = 0;
+      c->copy (empty_byte);
+    }
+    return_trace (true);
+  }
+
+  /* Byte region(s) per glyph to output
+     unpadded, hints removed if so requested
+     If we fail to process a glyph we produce an empty (0-length) glyph */
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+
+    glyf *glyf_prime = c->serializer->start_embed <glyf> ();
+    if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
+
+    hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
+    _populate_subset_glyphs (c->plan, &glyphs);
+
+    auto padded_offsets =
+    + hb_iter (glyphs)
+    | hb_map (&glyf_impl::SubsetGlyph::padded_size)
+    ;
+
+    unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0);
+    bool use_short_loca = max_offset < 0x1FFFF;
+
+
+    glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
+    if (!use_short_loca) {
+      padded_offsets =
+          + hb_iter (glyphs)
+          | hb_map (&glyf_impl::SubsetGlyph::length)
+          ;
+    }
+
+
+    if (unlikely (c->serializer->in_error ())) return_trace (false);
+    return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan,
+									       padded_offsets,
+									       use_short_loca)));
+  }
+
+  void
+  _populate_subset_glyphs (const hb_subset_plan_t   *plan,
+			   hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const;
+
+  protected:
+  UnsizedArrayOf<HBUINT8>
+		dataZ;	/* Glyphs data. */
+  public:
+  DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
+			 * check the size externally, allow Null() object of it by
+			 * defining it _MIN instead. */
+};
+
+struct glyf_accelerator_t
+{
+  glyf_accelerator_t (hb_face_t *face)
+  {
+    short_offset = false;
+    num_glyphs = 0;
+    loca_table = nullptr;
+    glyf_table = nullptr;
+#ifndef HB_NO_VAR
+    gvar = nullptr;
+#endif
+    hmtx = nullptr;
+#ifndef HB_NO_VERTICAL
+    vmtx = nullptr;
+#endif
+    const OT::head &head = *face->table.head;
+    if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0)
+      /* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
+      return;
+    short_offset = 0 == head.indexToLocFormat;
+
+    loca_table = face->table.loca.get_blob (); // Needs no destruct!
+    glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
+#ifndef HB_NO_VAR
+    gvar = face->table.gvar;
+#endif
+    hmtx = face->table.hmtx;
+#ifndef HB_NO_VERTICAL
+    vmtx = face->table.vmtx;
+#endif
+
+    num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
+    num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
+  }
+  ~glyf_accelerator_t ()
+  {
+    glyf_table.destroy ();
+  }
+
+  bool has_data () const { return num_glyphs; }
+
+  protected:
+  template<typename T>
+  bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const
+  {
+    if (gid >= num_glyphs) return false;
+
+    /* Making this allocfree is not that easy
+       https://github.com/harfbuzz/harfbuzz/issues/2095
+       mostly because of gvar handling in VF fonts,
+       perhaps a separate path for non-VF fonts can be considered */
+    contour_point_vector_t all_points;
+
+    bool phantom_only = !consumer.is_consuming_contour_points ();
+    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only)))
+      return false;
+
+    if (consumer.is_consuming_contour_points ())
+    {
+      unsigned count = all_points.length;
+      assert (count >= glyf_impl::PHANTOM_COUNT);
+      count -= glyf_impl::PHANTOM_COUNT;
+      for (unsigned point_index = 0; point_index < count; point_index++)
+	consumer.consume_point (all_points[point_index]);
+      consumer.points_end ();
+    }
+
+    /* Where to write phantoms, nullptr if not requested */
+    contour_point_t *phantoms = consumer.get_phantoms_sink ();
+    if (phantoms)
+      for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
+	phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i];
+
+    return true;
+  }
+
+#ifndef HB_NO_VAR
+  struct points_aggregator_t
+  {
+    hb_font_t *font;
+    hb_glyph_extents_t *extents;
+    contour_point_t *phantoms;
+
+    struct contour_bounds_t
+    {
+      contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
+
+      void add (const contour_point_t &p)
+      {
+	min_x = hb_min (min_x, p.x);
+	min_y = hb_min (min_y, p.y);
+	max_x = hb_max (max_x, p.x);
+	max_y = hb_max (max_y, p.y);
+      }
+
+      bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
+
+      void get_extents (hb_font_t *font, hb_glyph_extents_t *extents)
+      {
+	if (unlikely (empty ()))
+	{
+	  extents->width = 0;
+	  extents->x_bearing = 0;
+	  extents->height = 0;
+	  extents->y_bearing = 0;
+	  return;
+	}
+	extents->x_bearing = font->em_scalef_x (min_x);
+	extents->width = font->em_scalef_x (max_x) - extents->x_bearing;
+	extents->y_bearing = font->em_scalef_y (max_y);
+	extents->height = font->em_scalef_y (min_y) - extents->y_bearing;
+      }
+
+      protected:
+      float min_x, min_y, max_x, max_y;
+    } bounds;
+
+    points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_)
+    {
+      font = font_;
+      extents = extents_;
+      phantoms = phantoms_;
+      if (extents) bounds = contour_bounds_t ();
+    }
+
+    void consume_point (const contour_point_t &point) { bounds.add (point); }
+    void points_end () { bounds.get_extents (font, extents); }
+
+    bool is_consuming_contour_points () { return extents; }
+    contour_point_t *get_phantoms_sink () { return phantoms; }
+  };
+
+  public:
+  unsigned
+  get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
+  {
+    if (unlikely (gid >= num_glyphs)) return 0;
+
+    bool success = false;
+
+    contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
+    if (likely (font->num_coords == gvar->get_axis_count ()))
+      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms));
+
+    if (unlikely (!success))
+      return
+#ifndef HB_NO_VERTICAL
+	is_vertical ? vmtx->get_advance (gid) :
+#endif
+	hmtx->get_advance (gid);
+
+    float result = is_vertical
+		 ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
+		 : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x;
+    return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
+  }
+
+  int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
+  {
+    if (unlikely (gid >= num_glyphs)) return 0;
+
+    hb_glyph_extents_t extents;
+
+    contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
+    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms))))
+      return
+#ifndef HB_NO_VERTICAL
+	is_vertical ? vmtx->get_side_bearing (gid) :
+#endif
+	hmtx->get_side_bearing (gid);
+
+    return is_vertical
+	 ? ceilf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing
+	 : floorf (phantoms[glyf_impl::PHANTOM_LEFT].x);
+  }
+#endif
+
+  public:
+  bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
+  {
+    if (unlikely (gid >= num_glyphs)) return false;
+
+#ifndef HB_NO_VAR
+    if (font->num_coords)
+      return get_points (font, gid, points_aggregator_t (font, extents, nullptr));
+#endif
+    return glyph_for_gid (gid).get_extents (font, *this, extents);
+  }
+
+  const glyf_impl::Glyph
+  glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
+  {
+    if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph ();
+
+    unsigned int start_offset, end_offset;
+
+    if (short_offset)
+    {
+      const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
+      start_offset = 2 * offsets[gid];
+      end_offset   = 2 * offsets[gid + 1];
+    }
+    else
+    {
+      const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
+      start_offset = offsets[gid];
+      end_offset   = offsets[gid + 1];
+    }
+
+    if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
+      return glyf_impl::Glyph ();
+
+    glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
+			     end_offset - start_offset), gid);
+    return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph;
+  }
+
+  bool
+  get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
+  { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); }
+
+#ifndef HB_NO_VAR
+  const gvar_accelerator_t *gvar;
+#endif
+  const hmtx_accelerator_t *hmtx;
+#ifndef HB_NO_VERTICAL
+  const vmtx_accelerator_t *vmtx;
+#endif
+
+  private:
+  bool short_offset;
+  unsigned int num_glyphs;
+  hb_blob_ptr_t<loca> loca_table;
+  hb_blob_ptr_t<glyf> glyf_table;
+};
+
+
+inline void
+glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
+			       hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const
+{
+  OT::glyf_accelerator_t glyf (plan->source);
+
+  + hb_range (plan->num_output_glyphs ())
+  | hb_map ([&] (hb_codepoint_t new_gid)
+	{
+	  glyf_impl::SubsetGlyph subset_glyph = {0};
+	  subset_glyph.new_gid = new_gid;
+
+	  /* should never fail: all old gids should be mapped */
+	  if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid))
+	    return subset_glyph;
+
+	  if (new_gid == 0 &&
+	      !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
+	    subset_glyph.source_glyph = glyf_impl::Glyph ();
+	  else
+	    subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
+	  if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+	    subset_glyph.drop_hints_bytes ();
+	  else
+	    subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
+	  return subset_glyph;
+	})
+  | hb_sink (glyphs)
+  ;
+}
+
+
+
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_GLYF_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/loca.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/loca.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4481cba8ed2d58f6d8c30d6f195200571e96769a
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/loca.hh
@@ -0,0 +1,43 @@
+#ifndef OT_GLYF_LOCA_HH
+#define OT_GLYF_LOCA_HH
+
+
+#include "../../hb-open-type.hh"
+
+
+namespace OT {
+
+
+/*
+ * loca -- Index to Location
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/loca
+ */
+#define HB_OT_TAG_loca HB_TAG('l','o','c','a')
+
+struct loca
+{
+  friend struct glyf;
+  friend struct glyf_accelerator_t;
+
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_loca;
+
+  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (true);
+  }
+
+  protected:
+  UnsizedArrayOf<HBUINT8>
+		dataZ;	/* Location data. */
+  public:
+  DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
+			 * check the size externally, allow Null() object of it by
+			 * defining it _MIN instead. */
+};
+
+
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_LOCA_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/path-builder.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/path-builder.hh
new file mode 100644
index 0000000000000000000000000000000000000000..9bfc45a1a6078c35e1e025e3f08350d329498e25
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/path-builder.hh
@@ -0,0 +1,135 @@
+#ifndef OT_GLYF_PATH_BUILDER_HH
+#define OT_GLYF_PATH_BUILDER_HH
+
+
+#include "../../hb.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+struct path_builder_t
+{
+  hb_font_t *font;
+  hb_draw_session_t *draw_session;
+
+  struct optional_point_t
+  {
+    optional_point_t () {}
+    optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {}
+    operator bool () const { return has_data; }
+
+    bool has_data = false;
+    float x = 0.;
+    float y = 0.;
+
+    optional_point_t lerp (optional_point_t p, float t)
+    { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
+  } first_oncurve, first_offcurve, last_offcurve;
+
+  path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
+  {
+    font = font_;
+    draw_session = &draw_session_;
+    first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+  }
+
+  /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
+     See also:
+     * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
+     * https://stackoverflow.com/a/20772557 */
+  void consume_point (const contour_point_t &point)
+  {
+    bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
+    optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
+    if (!first_oncurve)
+    {
+      if (is_on_curve)
+      {
+	first_oncurve = p;
+	draw_session->move_to (p.x, p.y);
+      }
+      else
+      {
+	if (first_offcurve)
+	{
+	  optional_point_t mid = first_offcurve.lerp (p, .5f);
+	  first_oncurve = mid;
+	  last_offcurve = p;
+	  draw_session->move_to (mid.x, mid.y);
+	}
+	else
+	  first_offcurve = p;
+      }
+    }
+    else
+    {
+      if (last_offcurve)
+      {
+	if (is_on_curve)
+	{
+	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				     p.x, p.y);
+	  last_offcurve = optional_point_t ();
+	}
+	else
+	{
+	  optional_point_t mid = last_offcurve.lerp (p, .5f);
+	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				     mid.x, mid.y);
+	  last_offcurve = p;
+	}
+      }
+      else
+      {
+	if (is_on_curve)
+	  draw_session->line_to (p.x, p.y);
+	else
+	  last_offcurve = p;
+      }
+    }
+
+    if (point.is_end_point)
+    {
+      if (first_offcurve && last_offcurve)
+      {
+	optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
+	draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				   mid.x, mid.y);
+	last_offcurve = optional_point_t ();
+	/* now check the rest */
+      }
+
+      if (first_offcurve && first_oncurve)
+	draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
+				   first_oncurve.x, first_oncurve.y);
+      else if (last_offcurve && first_oncurve)
+	draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				   first_oncurve.x, first_oncurve.y);
+      else if (first_oncurve)
+	draw_session->line_to (first_oncurve.x, first_oncurve.y);
+      else if (first_offcurve)
+      {
+	float x = first_offcurve.x, y = first_offcurve.y;
+	draw_session->move_to (x, y);
+	draw_session->quadratic_to (x, y, x, y);
+      }
+
+      /* Getting ready for the next contour */
+      first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+      draw_session->close_path ();
+    }
+  }
+  void points_end () {}
+
+  bool is_consuming_contour_points () { return true; }
+  contour_point_t *get_phantoms_sink () { return nullptr; }
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_PATH_BUILDER_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/check-c-linkage-decls.py b/source/libs/harfbuzz/harfbuzz-src/src/check-c-linkage-decls.py
index 49cd29628c64c128ad9d5a3477d0c36db8139856..fe18eda897f375aedaa8ad7f6a525cf206373d2c 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/check-c-linkage-decls.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/check-c-linkage-decls.py
@@ -2,18 +2,20 @@
 
 import sys, os
 
-os.chdir (os.getenv ('srcdir', os.path.dirname (__file__)))
+srcdir = os.getenv ('srcdir', os.path.dirname (__file__))
+base_srcdir = os.getenv ('base_srcdir', srcdir)
+
+os.chdir (srcdir)
+
+def removeprefix(s):
+	abs_path = os.path.join(base_srcdir, s)
+	return os.path.relpath(abs_path, srcdir)
 
-def removeprefix(s, prefix):
-    if s.startswith(prefix):
-        return s[len(prefix):]
-    else:
-        return s[:]
 
 HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \
 	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')]
 HBSOURCES = [
-    removeprefix(x, 'src%s' % os.path.sep) for x in os.getenv ('HBSOURCES', '').split ()
+    removeprefix(x) for x in os.getenv ('HBSOURCES', '').split ()
 ] or [
     x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh'))
 ]
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/check-header-guards.py b/source/libs/harfbuzz/harfbuzz-src/src/check-header-guards.py
index b45ffe1addd94dfbec34460d117d2e16d4fbfa06..35ae6bef62539aa68a86ce7295494c5a507b091f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/check-header-guards.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/check-header-guards.py
@@ -2,19 +2,20 @@
 
 import sys, os, re
 
-os.chdir (os.getenv ('srcdir', os.path.dirname (__file__)))
+srcdir = os.getenv ('srcdir', os.path.dirname (__file__))
+base_srcdir = os.getenv ('base_srcdir', srcdir)
 
-def removeprefix(s, prefix):
-    if s.startswith(prefix):
-        return s[len(prefix):]
-    else:
-        return s[:]
+os.chdir (srcdir)
+
+def removeprefix(s):
+	abs_path = os.path.join(base_srcdir, s)
+	return os.path.relpath(abs_path, srcdir)
 
 
 HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \
 	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')]
 HBSOURCES = [
-    removeprefix(x, 'src%s' % os.path.sep) for x in os.getenv ('HBSOURCES', '').split ()
+    removeprefix(x) for x in os.getenv ('HBSOURCES', '').split ()
 ] or [
     x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh'))
 ]
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/check-includes.py b/source/libs/harfbuzz/harfbuzz-src/src/check-includes.py
index ed201aa3e0a06a2abf15e21a60691b288da4d9cf..fc95874b12bd9c240407d54245d94f527b6f6bbe 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/check-includes.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/check-includes.py
@@ -2,19 +2,20 @@
 
 import sys, os, re
 
-os.chdir (os.getenv ('srcdir', os.path.dirname (__file__)))
+srcdir = os.getenv ('srcdir', os.path.dirname (__file__))
+base_srcdir = os.getenv ('base_srcdir', srcdir)
 
-def removeprefix(s, prefix):
-    if s.startswith(prefix):
-        return s[len(prefix):]
-    else:
-        return s[:]
+os.chdir (srcdir)
+
+def removeprefix(s):
+	abs_path = os.path.join(base_srcdir, s)
+	return os.path.relpath(abs_path, srcdir)
 
 HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \
 	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')]
 
 HBSOURCES = [
-    removeprefix(x, 'src%s' % os.path.sep) for x in os.getenv ('HBSOURCES', '').split ()
+    removeprefix(x) for x in os.getenv ('HBSOURCES', '').split ()
 ] or [
     x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh'))
 ]
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/check-symbols.py b/source/libs/harfbuzz/harfbuzz-src/src/check-symbols.py
index 385959bdef781aabe4f392480855a2fc97f5e099..11ca28dc7f46192b71c24e42ee15d26e688e2f13 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/check-symbols.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/check-symbols.py
@@ -17,7 +17,7 @@ if not nm:
 	print ('check-symbols.py: \'nm\' not found; skipping test')
 	sys.exit (77)
 
-cxxflit = shutil.which ('c++filt')
+cxxfilt = shutil.which ('c++filt')
 
 tested = False
 stat = 0
@@ -34,10 +34,10 @@ for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject'
 				    for s in re.findall (r'^.+ [BCDGIRST] .+$', subprocess.check_output (nm.split() + [so]).decode ('utf-8'), re.MULTILINE)
 				    if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)]
 
-		# run again c++flit also if is available
-		if cxxflit:
+		# run again c++filt also if is available
+		if cxxfilt:
 			EXPORTED_SYMBOLS = subprocess.check_output (
-				[cxxflit], input='\n'.join (EXPORTED_SYMBOLS).encode ()
+				[cxxfilt], input='\n'.join (EXPORTED_SYMBOLS).encode ()
 			).decode ('utf-8').splitlines ()
 
 		prefix = (symprefix + os.path.basename (so)).replace ('libharfbuzz', 'hb').replace ('-', '_').split ('.')[0]
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-joining-list.py b/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-joining-list.py
index 8162a4a3e268fa084032cec5f563dddb7e091262..7ec7425fd8cc79cbe168623e40c8de59b9242a8a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-joining-list.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-joining-list.py
@@ -94,13 +94,13 @@ for h in headers:
 		print (" * %s" % (l.strip ()))
 print (" */")
 print ()
-print ("#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH")
-print ("#define HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH")
+print ("#ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH")
+print ("#define HB_OT_SHAPER_ARABIC_JOINING_LIST_HH")
 print ()
 
 print_has_arabic_joining (read (files[1]), read_joining_uu (files[0]))
 
 print ()
-print ("#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH */")
+print ("#endif /* HB_OT_SHAPER_ARABIC_JOINING_LIST_HH */")
 print ()
 print ("/* == End of generated function == */")
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-table.py b/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-table.py
index 621d5b60c89ee303ae4350a1a279d73be57ac9ed..8278d7d69c012facf3102655d8a6c1e960b50b54 100755
--- a/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-table.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/gen-arabic-table.py
@@ -153,12 +153,29 @@ def print_joining_table(f):
 		print ("#undef %s" % (short))
 	print ()
 
+LIGATURES = (
+	0xF2EE, 0xFC08, 0xFC0E, 0xFC12, 0xFC32, 0xFC3F, 0xFC40, 0xFC41, 0xFC42,
+	0xFC44, 0xFC4E, 0xFC5E, 0xFC60, 0xFC61, 0xFC62, 0xFC6A, 0xFC6D, 0xFC6F,
+	0xFC70, 0xFC73, 0xFC75, 0xFC86, 0xFC8F, 0xFC91, 0xFC94, 0xFC9C, 0xFC9D,
+	0xFC9E, 0xFC9F, 0xFCA1, 0xFCA2, 0xFCA3, 0xFCA4, 0xFCA8, 0xFCAA, 0xFCAC,
+	0xFCB0, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE, 0xFCCF, 0xFCD0,
+	0xFCD1, 0xFCD2, 0xFCD3, 0xFCD5, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, 0xFD30,
+	0xFD88, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC,
+	0xF201, 0xF211, 0xF2EE,
+)
+
 def print_shaping_table(f):
 
 	shapes = {}
 	ligatures = {}
 	names = {}
-	for line in f:
+	lines = f.readlines()
+	lines += [
+		"F201;PUA ARABIC LIGATURE LELLAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 0644 0647;;;;N;;;;;",
+		"F211;PUA ARABIC LIGATURE LAM WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062C;;;;N;;;;;",
+		"F2EE;PUA ARABIC LIGATURE SHADDA WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B 0651;;;;N;;;;;",
+	]
+	for line in lines:
 
 		fields = [x.strip () for x in line.split (';')]
 		if fields[5][0:1] != '<':
@@ -166,14 +183,19 @@ def print_shaping_table(f):
 
 		items = fields[5].split (' ')
 		shape, items = items[0][1:-1], tuple (int (x, 16) for x in items[1:])
+		c = int (fields[0], 16)
 
 		if not shape in ['initial', 'medial', 'isolated', 'final']:
 			continue
 
-		c = int (fields[0], 16)
 		if len (items) != 1:
-			# We only care about lam-alef ligatures
-			if len (items) != 2 or items[0] != 0x0644 or items[1] not in [0x0622, 0x0623, 0x0625, 0x0627]:
+			# Mark ligatures start with space and are in visual order, so we
+			# remove the space and reverse the items.
+			if items[0] == 0x0020:
+				items = items[:0:-1]
+				shape = None
+			# We only care about a subset of ligatures
+			if c not in LIGATURES:
 				continue
 
 			# Save ligature
@@ -209,34 +231,99 @@ def print_shaping_table(f):
 	print ("#define SHAPING_TABLE_LAST	0x%04Xu" % max_u)
 	print ()
 
-	ligas = {}
-	for pair in ligatures.keys ():
-		for shape in ligatures[pair]:
-			c = ligatures[pair][shape]
-			if shape == 'isolated':
-				liga = (shapes[pair[0]]['initial'], shapes[pair[1]]['final'])
-			elif shape == 'final':
-				liga = (shapes[pair[0]]['medial'], shapes[pair[1]]['final'])
+	ligas_2 = {}
+	ligas_3 = {}
+	ligas_mark_2 = {}
+	for key in ligatures.keys ():
+		for shape in ligatures[key]:
+			c = ligatures[key][shape]
+			if len(key) == 3:
+				if shape == 'isolated':
+					liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial'], shapes[key[2]]['final'])
+				elif shape == 'final':
+					liga = (shapes[key[0]]['medial'], shapes[key[1]]['medial'], shapes[key[2]]['final'])
+				elif shape == 'initial':
+					liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial'], shapes[key[2]]['medial'])
+				else:
+					raise Exception ("Unexpected shape", shape)
+				if liga[0] not in ligas_3:
+					ligas_3[liga[0]] = []
+				ligas_3[liga[0]].append ((liga[1], liga[2], c))
+			elif len(key) == 2:
+				if shape is None:
+					liga = key
+					if liga[0] not in ligas_mark_2:
+						ligas_mark_2[liga[0]] = []
+					ligas_mark_2[liga[0]].append ((liga[1], c))
+					continue
+				elif shape == 'isolated':
+					liga = (shapes[key[0]]['initial'], shapes[key[1]]['final'])
+				elif shape == 'final':
+					liga = (shapes[key[0]]['medial'], shapes[key[1]]['final'])
+				elif shape == 'initial':
+					liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial'])
+				else:
+					raise Exception ("Unexpected shape", shape)
+				if liga[0] not in ligas_2:
+					ligas_2[liga[0]] = []
+				ligas_2[liga[0]].append ((liga[1], c))
 			else:
-				raise Exception ("Unexpected shape", shape)
-			if liga[0] not in ligas:
-				ligas[liga[0]] = []
-			ligas[liga[0]].append ((liga[1], c))
-	max_i = max (len (ligas[l]) for l in ligas)
+				raise Exception ("Unexpected number of ligature components", key)
+	max_i = max (len (ligas_2[l]) for l in ligas_2)
 	print ()
 	print ("static const struct ligature_set_t {")
 	print (" uint16_t first;")
 	print (" struct ligature_pairs_t {")
-	print ("   uint16_t second;")
+	print ("   uint16_t components[1];")
 	print ("   uint16_t ligature;")
 	print (" } ligatures[%d];" % max_i)
 	print ("} ligature_table[] =")
 	print ("{")
-	for first in sorted (ligas.keys ()):
+	for first in sorted (ligas_2.keys ()):
+
+		print ("  { 0x%04Xu, {" % (first))
+		for liga in ligas_2[first]:
+			print ("    { {0x%04Xu}, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]]))
+		print ("  }},")
+
+	print ("};")
+	print ()
+
+	max_i = max (len (ligas_mark_2[l]) for l in ligas_mark_2)
+	print ()
+	print ("static const struct ligature_mark_set_t {")
+	print (" uint16_t first;")
+	print (" struct ligature_pairs_t {")
+	print ("   uint16_t components[1];")
+	print ("   uint16_t ligature;")
+	print (" } ligatures[%d];" % max_i)
+	print ("} ligature_mark_table[] =")
+	print ("{")
+	for first in sorted (ligas_mark_2.keys ()):
+
+		print ("  { 0x%04Xu, {" % (first))
+		for liga in ligas_mark_2[first]:
+			print ("    { {0x%04Xu}, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]]))
+		print ("  }},")
+
+	print ("};")
+	print ()
+
+	max_i = max (len (ligas_3[l]) for l in ligas_3)
+	print ()
+	print ("static const struct ligature_3_set_t {")
+	print (" uint16_t first;")
+	print (" struct ligature_triplets_t {")
+	print ("   uint16_t components[2];")
+	print ("   uint16_t ligature;")
+	print (" } ligatures[%d];" % max_i)
+	print ("} ligature_3_table[] =")
+	print ("{")
+	for first in sorted (ligas_3.keys ()):
 
 		print ("  { 0x%04Xu, {" % (first))
-		for liga in ligas[first]:
-			print ("    { 0x%04Xu, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]]))
+		for liga in ligas_3[first]:
+			print ("    { {0x%04Xu, 0x%04Xu}, 0x%04Xu}, /* %s */" % (liga[0], liga[1], liga[2], names[liga[2]]))
 		print ("  }},")
 
 	print ("};")
@@ -257,8 +344,8 @@ for h in headers:
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
-print ("#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH")
-print ("#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH")
+print ("#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH")
+print ("#define HB_OT_SHAPER_ARABIC_TABLE_HH")
 print ()
 
 read_blocks (files[2])
@@ -266,6 +353,6 @@ print_joining_table (files[0])
 print_shaping_table (files[1])
 
 print ()
-print ("#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */")
+print ("#endif /* HB_OT_SHAPER_ARABIC_TABLE_HH */")
 print ()
 print ("/* == End of generated table == */")
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/gen-indic-table.py b/source/libs/harfbuzz/harfbuzz-src/src/gen-indic-table.py
index 367e55e272367a57ce25c1ff1c44733056457bdb..0ff3ef4ac2fa04ccbf606a0d6441978bb5eea15e 100755
--- a/source/libs/harfbuzz/harfbuzz-src/src/gen-indic-table.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/gen-indic-table.py
@@ -26,7 +26,6 @@ ALLOWED_BLOCKS = [
 	'Telugu',
 	'Kannada',
 	'Malayalam',
-	'Sinhala',
 	'Myanmar',
 	'Khmer',
 	'Vedic Extensions',
@@ -41,8 +40,7 @@ files = [open (x, encoding='utf-8') for x in sys.argv[1:]]
 
 headers = [[f.readline () for i in range (2)] for f in files]
 
-data = [{} for _ in files]
-values = [{} for _ in files]
+unicode_data = [{} for _ in files]
 for i, f in enumerate (files):
 	for line in f:
 
@@ -64,15 +62,12 @@ for i, f in enumerate (files):
 		t = fields[1]
 
 		for u in range (start, end + 1):
-			data[i][u] = t
-		values[i][t] = values[i].get (t, 0) + end - start + 1
+			unicode_data[i][u] = t
 
 # Merge data into one dict:
 defaults = ('Other', 'Not_Applicable', 'No_Block')
-for i,v in enumerate (defaults):
-	values[i][v] = values[i].get (v, 0) + 1
 combined = {}
-for i,d in enumerate (data):
+for i,d in enumerate (unicode_data):
 	for u,v in d.items ():
 		if i == 2 and not u in combined:
 			continue
@@ -80,14 +75,412 @@ for i,d in enumerate (data):
 			combined[u] = list (defaults)
 		combined[u][i] = v
 combined = {k:v for k,v in combined.items() if k in ALLOWED_SINGLES or v[2] in ALLOWED_BLOCKS}
-data = combined
-del combined
+
+
+# Convert categories & positions types
+
+categories = {
+  'indic' : [
+    'X',
+    'C',
+    'V',
+    'N',
+    'H',
+    'ZWNJ',
+    'ZWJ',
+    'M',
+    'SM',
+    'A',
+    'VD',
+    'PLACEHOLDER',
+    'DOTTEDCIRCLE',
+    'RS',
+    'Repha',
+    'Ra',
+    'CM',
+    'Symbol',
+    'CS',
+  ],
+  'khmer' : [
+    'VAbv',
+    'VBlw',
+    'VPre',
+    'VPst',
+
+    'Robatic',
+    'Xgroup',
+    'Ygroup',
+  ],
+  'myanmar' : [
+    'VAbv',
+    'VBlw',
+    'VPre',
+    'VPst',
+
+    'IV',
+    'As',
+    'DB',
+    'GB',
+    'MH',
+    'MR',
+    'MW',
+    'MY',
+    'PT',
+    'VS',
+    'ML',
+  ],
+}
+
+category_map = {
+  'Other'			: 'X',
+  'Avagraha'			: 'Symbol',
+  'Bindu'			: 'SM',
+  'Brahmi_Joining_Number'	: 'PLACEHOLDER', # Don't care.
+  'Cantillation_Mark'		: 'A',
+  'Consonant'			: 'C',
+  'Consonant_Dead'		: 'C',
+  'Consonant_Final'		: 'CM',
+  'Consonant_Head_Letter'	: 'C',
+  'Consonant_Initial_Postfixed'	: 'C', # TODO
+  'Consonant_Killer'		: 'M', # U+17CD only.
+  'Consonant_Medial'		: 'CM',
+  'Consonant_Placeholder'	: 'PLACEHOLDER',
+  'Consonant_Preceding_Repha'	: 'Repha',
+  'Consonant_Prefixed'		: 'X', # Don't care.
+  'Consonant_Subjoined'		: 'CM',
+  'Consonant_Succeeding_Repha'	: 'CM',
+  'Consonant_With_Stacker'	: 'CS',
+  'Gemination_Mark'		: 'SM', # https://github.com/harfbuzz/harfbuzz/issues/552
+  'Invisible_Stacker'		: 'H',
+  'Joiner'			: 'ZWJ',
+  'Modifying_Letter'		: 'X',
+  'Non_Joiner'			: 'ZWNJ',
+  'Nukta'			: 'N',
+  'Number'			: 'PLACEHOLDER',
+  'Number_Joiner'		: 'PLACEHOLDER', # Don't care.
+  'Pure_Killer'			: 'M', # Is like a vowel matra.
+  'Register_Shifter'		: 'RS',
+  'Syllable_Modifier'		: 'SM',
+  'Tone_Letter'			: 'X',
+  'Tone_Mark'			: 'N',
+  'Virama'			: 'H',
+  'Visarga'			: 'SM',
+  'Vowel'			: 'V',
+  'Vowel_Dependent'		: 'M',
+  'Vowel_Independent'		: 'V',
+  'Dotted_Circle'		: 'DOTTEDCIRCLE', # Ours, not Unicode's
+  'Ra'				: 'Ra', # Ours, not Unicode's
+}
+position_map = {
+  'Not_Applicable'		: 'END',
+
+  'Left'			: 'PRE_C',
+  'Top'				: 'ABOVE_C',
+  'Bottom'			: 'BELOW_C',
+  'Right'			: 'POST_C',
+
+  # These should resolve to the position of the last part of the split sequence.
+  'Bottom_And_Right'		: 'POST_C',
+  'Left_And_Right'		: 'POST_C',
+  'Top_And_Bottom'		: 'BELOW_C',
+  'Top_And_Bottom_And_Left'	: 'BELOW_C',
+  'Top_And_Bottom_And_Right'	: 'POST_C',
+  'Top_And_Left'		: 'ABOVE_C',
+  'Top_And_Left_And_Right'	: 'POST_C',
+  'Top_And_Right'		: 'POST_C',
+
+  'Overstruck'			: 'AFTER_MAIN',
+  'Visual_order_left'		: 'PRE_M',
+}
+
+category_overrides = {
+
+  # These are the variation-selectors. They only appear in the Myanmar grammar
+  # but are not Myanmar-specific
+  0xFE00: 'VS',
+  0xFE01: 'VS',
+  0xFE02: 'VS',
+  0xFE03: 'VS',
+  0xFE04: 'VS',
+  0xFE05: 'VS',
+  0xFE06: 'VS',
+  0xFE07: 'VS',
+  0xFE08: 'VS',
+  0xFE09: 'VS',
+  0xFE0A: 'VS',
+  0xFE0B: 'VS',
+  0xFE0C: 'VS',
+  0xFE0D: 'VS',
+  0xFE0E: 'VS',
+  0xFE0F: 'VS',
+
+  # These appear in the OT Myanmar spec, but are not Myanmar-specific
+  0x2015: 'PLACEHOLDER',
+  0x2022: 'PLACEHOLDER',
+  0x25FB: 'PLACEHOLDER',
+  0x25FC: 'PLACEHOLDER',
+  0x25FD: 'PLACEHOLDER',
+  0x25FE: 'PLACEHOLDER',
+
+
+  # Indic
+
+  0x0930: 'Ra', # Devanagari
+  0x09B0: 'Ra', # Bengali
+  0x09F0: 'Ra', # Bengali
+  0x0A30: 'Ra', # Gurmukhi 	No Reph
+  0x0AB0: 'Ra', # Gujarati
+  0x0B30: 'Ra', # Oriya
+  0x0BB0: 'Ra', # Tamil 	No Reph
+  0x0C30: 'Ra', # Telugu 	Reph formed only with ZWJ
+  0x0CB0: 'Ra', # Kannada
+  0x0D30: 'Ra', # Malayalam 	No Reph, Logical Repha
+
+  # The following act more like the Bindus.
+  0x0953: 'SM',
+  0x0954: 'SM',
+
+  # The following act like consonants.
+  0x0A72: 'C',
+  0x0A73: 'C',
+  0x1CF5: 'C',
+  0x1CF6: 'C',
+
+  # TODO: The following should only be allowed after a Visarga.
+  # For now, just treat them like regular tone marks.
+  0x1CE2: 'A',
+  0x1CE3: 'A',
+  0x1CE4: 'A',
+  0x1CE5: 'A',
+  0x1CE6: 'A',
+  0x1CE7: 'A',
+  0x1CE8: 'A',
+
+  # TODO: The following should only be allowed after some of
+  # the nasalization marks, maybe only for U+1CE9..U+1CF1.
+  # For now, just treat them like tone marks.
+  0x1CED: 'A',
+
+  # The following take marks in standalone clusters, similar to Avagraha.
+  0xA8F2: 'Symbol',
+  0xA8F3: 'Symbol',
+  0xA8F4: 'Symbol',
+  0xA8F5: 'Symbol',
+  0xA8F6: 'Symbol',
+  0xA8F7: 'Symbol',
+  0x1CE9: 'Symbol',
+  0x1CEA: 'Symbol',
+  0x1CEB: 'Symbol',
+  0x1CEC: 'Symbol',
+  0x1CEE: 'Symbol',
+  0x1CEF: 'Symbol',
+  0x1CF0: 'Symbol',
+  0x1CF1: 'Symbol',
+
+  0x0A51: 'M', # https://github.com/harfbuzz/harfbuzz/issues/524
+
+  # According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil,
+  # so the Indic shaper needs to know their categories.
+  0x11301: 'SM',
+  0x11302: 'SM',
+  0x11303: 'SM',
+  0x1133B: 'N',
+  0x1133C: 'N',
+
+  0x0AFB: 'N', # https://github.com/harfbuzz/harfbuzz/issues/552
+  0x0B55: 'N', # https://github.com/harfbuzz/harfbuzz/issues/2849
+
+  0x09FC: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/1613
+  0x0C80: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/623
+  0x0D04: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/3511
+
+  0x25CC: 'DOTTEDCIRCLE',
+
+
+  # Khmer
+
+  0x179A: 'Ra',
+
+  0x17CC: 'Robatic',
+  0x17C9: 'Robatic',
+  0x17CA: 'Robatic',
+
+  0x17C6: 'Xgroup',
+  0x17CB: 'Xgroup',
+  0x17CD: 'Xgroup',
+  0x17CE: 'Xgroup',
+  0x17CF: 'Xgroup',
+  0x17D0: 'Xgroup',
+  0x17D1: 'Xgroup',
+
+  0x17C7: 'Ygroup',
+  0x17C8: 'Ygroup',
+  0x17DD: 'Ygroup',
+  0x17D3: 'Ygroup', # Just guessing. Uniscribe doesn't categorize it.
+
+  0x17D9: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/issues/2384
+
+
+  # Myanmar
+
+  # https://docs.microsoft.com/en-us/typography/script-development/myanmar#analyze
+
+  0x104E: 'C', # The spec says C, IndicSyllableCategory says Consonant_Placeholder
+
+  0x1004: 'Ra',
+  0x101B: 'Ra',
+  0x105A: 'Ra',
+
+  0x1032: 'A',
+  0x1036: 'A',
+
+  0x103A: 'As',
+
+  #0x1040: 'D0', # XXX The spec says D0, but Uniscribe doesn't seem to do.
+
+  0x103E: 'MH',
+  0x1060: 'ML',
+  0x103C: 'MR',
+  0x103D: 'MW',
+  0x1082: 'MW',
+  0x103B: 'MY',
+  0x105E: 'MY',
+  0x105F: 'MY',
+
+  0x1063: 'PT',
+  0x1064: 'PT',
+  0x1069: 'PT',
+  0x106A: 'PT',
+  0x106B: 'PT',
+  0x106C: 'PT',
+  0x106D: 'PT',
+  0xAA7B: 'PT',
+
+  0x1038: 'SM',
+  0x1087: 'SM',
+  0x1088: 'SM',
+  0x1089: 'SM',
+  0x108A: 'SM',
+  0x108B: 'SM',
+  0x108C: 'SM',
+  0x108D: 'SM',
+  0x108F: 'SM',
+  0x109A: 'SM',
+  0x109B: 'SM',
+  0x109C: 'SM',
+
+  0x104A: 'PLACEHOLDER',
+}
+position_overrides = {
+
+  0x0A51: 'BELOW_C', # https://github.com/harfbuzz/harfbuzz/issues/524
+
+  0x0B01: 'BEFORE_SUB', # Oriya Bindu is BeforeSub in the spec.
+}
+
+def matra_pos_left(u, block):
+  return "PRE_M"
+def matra_pos_right(u, block):
+  if block == 'Devanagari':	return  'AFTER_SUB'
+  if block == 'Bengali':	return  'AFTER_POST'
+  if block == 'Gurmukhi':	return  'AFTER_POST'
+  if block == 'Gujarati':	return  'AFTER_POST'
+  if block == 'Oriya':		return  'AFTER_POST'
+  if block == 'Tamil':		return  'AFTER_POST'
+  if block == 'Telugu':		return  'BEFORE_SUB' if u <= 0x0C42 else 'AFTER_SUB'
+  if block == 'Kannada':	return  'BEFORE_SUB' if u < 0x0CC3 or u > 0x0CD6 else 'AFTER_SUB'
+  if block == 'Malayalam':	return  'AFTER_POST'
+  return 'AFTER_SUB'
+def matra_pos_top(u, block):
+  # BENG and MLYM don't have top matras.
+  if block == 'Devanagari':	return  'AFTER_SUB'
+  if block == 'Gurmukhi':	return  'AFTER_POST' # Deviate from spec
+  if block == 'Gujarati':	return  'AFTER_SUB'
+  if block == 'Oriya':		return  'AFTER_MAIN'
+  if block == 'Tamil':		return  'AFTER_SUB'
+  if block == 'Telugu':		return  'BEFORE_SUB'
+  if block == 'Kannada':	return  'BEFORE_SUB'
+  return 'AFTER_SUB'
+def matra_pos_bottom(u, block):
+  if block == 'Devanagari':	return  'AFTER_SUB'
+  if block == 'Bengali':	return  'AFTER_SUB'
+  if block == 'Gurmukhi':	return  'AFTER_POST'
+  if block == 'Gujarati':	return  'AFTER_POST'
+  if block == 'Oriya':		return  'AFTER_SUB'
+  if block == 'Tamil':		return  'AFTER_POST'
+  if block == 'Telugu':		return  'BEFORE_SUB'
+  if block == 'Kannada':	return  'BEFORE_SUB'
+  if block == 'Malayalam':	return  'AFTER_POST'
+  return "AFTER_SUB"
+def indic_matra_position(u, pos, block): # Reposition matra
+  if pos == 'PRE_C':	return matra_pos_left(u, block)
+  if pos == 'POST_C':	return matra_pos_right(u, block)
+  if pos == 'ABOVE_C':	return matra_pos_top(u, block)
+  if pos == 'BELOW_C':	return matra_pos_bottom(u, block)
+  assert (False)
+
+def position_to_category(pos):
+  if pos == 'PRE_C':	return 'VPre'
+  if pos == 'ABOVE_C':	return 'VAbv'
+  if pos == 'BELOW_C':	return 'VBlw'
+  if pos == 'POST_C':	return 'VPst'
+  assert(False)
+
+
+defaults = (category_map[defaults[0]], position_map[defaults[1]], defaults[2])
+
+indic_data = {}
+for k, (cat, pos, block) in combined.items():
+  cat = category_map[cat]
+  pos = position_map[pos]
+  indic_data[k] = (cat, pos, block)
+
+for k,new_cat in category_overrides.items():
+  (cat, pos, _) = indic_data.get(k, defaults)
+  indic_data[k] = (new_cat, pos, unicode_data[2][k])
+
+# We only expect position for certain types
+positioned_categories = ('CM', 'SM', 'RS', 'H', 'M')
+for k, (cat, pos, block) in indic_data.items():
+  if cat not in positioned_categories:
+    pos = 'END'
+    indic_data[k] = (cat, pos, block)
+
+# Position overrides are more complicated
+
+# Keep in sync with CONSONANT_FLAGS in the shaper
+consonant_categories = ('C', 'CS', 'Ra','CM', 'V', 'PLACEHOLDER', 'DOTTEDCIRCLE')
+smvd_categories = ('SM', 'VD', 'A', 'Symbol')
+for k, (cat, pos, block) in indic_data.items():
+  if cat in consonant_categories:
+    pos = 'BASE_C'
+  elif cat == 'M':
+    if block.startswith('Khmer') or block.startswith('Myanmar'):
+      cat = position_to_category(pos)
+    else:
+      pos = indic_matra_position(k, pos, block)
+  elif cat in smvd_categories:
+    pos = 'SMVD';
+  indic_data[k] = (cat, pos, block)
+
+for k,new_pos in position_overrides.items():
+  (cat, pos, _) = indic_data.get(k, defaults)
+  indic_data[k] = (cat, new_pos, unicode_data[2][k])
+
+
+values = [{_: 1} for _ in defaults]
+for vv in indic_data.values():
+  for i,v in enumerate(vv):
+    values[i][v] = values[i].get (v, 0) + 1
+
+
+
 
 # Move the outliers NO-BREAK SPACE and DOTTED CIRCLE out
 singles = {}
 for u in ALLOWED_SINGLES:
-	singles[u] = data[u]
-	del data[u]
+	singles[u] = indic_data[u]
+	del indic_data[u]
 
 print ("/* == Start of generated table == */")
 print ("/*")
@@ -106,37 +499,63 @@ print ('#include "hb.hh"')
 print ()
 print ('#ifndef HB_NO_OT_SHAPE')
 print ()
-print ('#include "hb-ot-shape-complex-indic.hh"')
+print ('#include "hb-ot-shaper-indic.hh"')
+print ()
+print ('#pragma GCC diagnostic push')
+print ('#pragma GCC diagnostic ignored "-Wunused-macros"')
+print ()
+
+# Print categories
+for shaper in categories:
+  print ('#include "hb-ot-shaper-%s-machine.hh"' % shaper)
+print ()
+done = {}
+for shaper, shaper_cats in categories.items():
+  print ('/* %s */' % shaper)
+  for cat in shaper_cats:
+    v = shaper[0].upper()
+    if cat not in done:
+      print ("#define OT_%s %s_Cat(%s)" % (cat, v, cat))
+      done[cat] = v
+    else:
+      print ('static_assert (OT_%s == %s_Cat(%s), "");' % (cat, v, cat))
 print ()
 
 # Shorten values
 short = [{
-	"Bindu":		'Bi',
-	"Cantillation_Mark":	'Ca',
-	"Joiner":		'ZWJ',
-	"Non_Joiner":		'ZWNJ',
-	"Number":		'Nd',
-	"Visarga":		'Vs',
-	"Vowel":		'Vo',
-	"Vowel_Dependent":	'M',
-	"Consonant_Prefixed":	'CPrf',
-	"Other":		'x',
+	"Repha":		'Rf',
+	"PLACEHOLDER":		'GB',
+	"DOTTEDCIRCLE":		'DC',
+	"VPst":			'VR',
+	"VPre":			'VL',
+	"Robatic":		'Rt',
+	"Xgroup":		'Xg',
+	"Ygroup":		'Yg',
+	"As":			'As',
 },{
-	"Not_Applicable":	'x',
+	"END":			'X',
+	"BASE_C":		'C',
+	"ABOVE_C":		'T',
+	"BELOW_C":		'B',
+	"POST_C":		'R',
+	"PRE_C":		'L',
+	"PRE_M":		'LM',
+	"AFTER_MAIN":		'A',
+	"AFTER_SUB":		'AS',
+	"BEFORE_SUB":		'BS',
+	"AFTER_POST":		'AP',
+	"SMVD":			'SM',
 }]
 all_shorts = [{},{}]
 
 # Add some of the values, to make them more readable, and to avoid duplicates
 
-
 for i in range (2):
 	for v,s in short[i].items ():
 		all_shorts[i][s] = v
 
-what = ["INDIC_SYLLABIC_CATEGORY", "INDIC_MATRA_CATEGORY"]
-what_short = ["ISC", "IMC"]
-print ('#pragma GCC diagnostic push')
-print ('#pragma GCC diagnostic ignored "-Wunused-macros"')
+what = ["OT", "POS"]
+what_short = ["_OT", "_POS"]
 cat_defs = []
 for i in range (2):
 	vv = sorted (values[i].keys ())
@@ -150,7 +569,7 @@ for i in range (2):
 				raise Exception ("Duplicate short value alias", v, all_shorts[i][s])
 			all_shorts[i][s] = v
 			short[i][v] = s
-		cat_defs.append ((what_short[i] + '_' + s, what[i] + '_' + v.upper (), str (values[i][v]), v))
+		cat_defs.append ((what_short[i] + '_' + s, what[i] + '_' + (v.upper () if i else v), str (values[i][v]), v))
 
 maxlen_s = max ([len (c[0]) for c in cat_defs])
 maxlen_l = max ([len (c[1]) for c in cat_defs])
@@ -163,7 +582,9 @@ for s in what_short:
 print ()
 print ('#pragma GCC diagnostic pop')
 print ()
-print ("#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)")
+print ("#define INDIC_COMBINE_CATEGORIES(S,M) ((S) | ((M) << 8))")
+print ()
+print ("#define _(S,M) INDIC_COMBINE_CATEGORIES (%s_##S, %s_##M)" % tuple(what_short))
 print ()
 print ()
 
@@ -193,7 +614,7 @@ def print_block (block, start, end, data):
 	if block:
 		last_block = block
 
-uu = sorted (data.keys ())
+uu = sorted (indic_data)
 
 last = -100000
 num = 0
@@ -204,17 +625,17 @@ print ("static const uint16_t indic_table[] = {")
 for u in uu:
 	if u <= last:
 		continue
-	block = data[u][2]
+	block = indic_data[u][2]
 
 	start = u//8*8
 	end = start+1
-	while end in uu and block == data[end][2]:
+	while end in uu and block == indic_data[end][2]:
 		end += 1
 	end = (end-1)//8*8 + 7
 
 	if start != last + 1:
 		if start - last <= 1+16*3:
-			print_block (None, last+1, start-1, data)
+			print_block (None, last+1, start-1, indic_data)
 		else:
 			if last >= 0:
 				ends.append (last + 1)
@@ -224,7 +645,7 @@ for u in uu:
 			print ("#define indic_offset_0x%04xu %d" % (start, offset))
 			starts.append (start)
 
-	print_block (block, start, end, data)
+	print_block (block, start, end, indic_data)
 	last = end
 ends.append (last + 1)
 offset += ends[-1] - starts[-1]
@@ -254,10 +675,11 @@ for p in sorted(pages):
 print ("    default:")
 print ("      break;")
 print ("  }")
-print ("  return _(x,x);")
+print ("  return _(X,X);")
 print ("}")
 print ()
 print ("#undef _")
+print ("#undef INDIC_COMBINE_CATEGORIES")
 for i in range (2):
 	print ()
 	vv = sorted (values[i].keys ())
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py b/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py
index f8fb05f11336e535cd58bf033e9c5cfdd20a68f9..7e15c08c56cab9c1b77a9484740925faef6bed3c 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py
@@ -894,7 +894,6 @@ print ()
 print ('#ifndef HB_OT_TAG_TABLE_HH')
 print ('#define HB_OT_TAG_TABLE_HH')
 print ()
-print ('static const LangTag ot_languages[] = {')
 
 def hb_tag (tag):
 	"""Convert a tag to ``HB_TAG`` form.
@@ -944,33 +943,39 @@ def get_matching_language_name (intersection, candidates):
 def same_tag (bcp_47_tag, ot_tags):
 	return len (bcp_47_tag) == 3 and len (ot_tags) == 1 and bcp_47_tag == ot_tags[0].lower ()
 
-for language, tags in sorted (ot.from_bcp_47.items ()):
-	if language == '' or '-' in language:
-		continue
-	commented_out = same_tag (language, tags)
-	for i, tag in enumerate (tags, start=1):
-		print ('%s{\"%s\",\t%s},' % ('/*' if commented_out else '  ', language, hb_tag (tag)), end='')
-		if commented_out:
-			print ('*/', end='')
-		print ('\t/* ', end='')
-		bcp_47_name = bcp_47.names.get (language, '')
-		bcp_47_name_candidates = bcp_47_name.split ('\n')
-		ot_name = ot.names[tag]
-		scope = bcp_47.scopes.get (language, '')
-		if tag == DEFAULT_LANGUAGE_SYSTEM:
-			write (f'{bcp_47_name_candidates[0]}{scope} != {ot.names[language.upper ()]}')
-		else:
-			intersection = language_name_intersection (bcp_47_name, ot_name)
-			if not intersection:
-				write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot_name))
+for language_len in (2, 3):
+	if language_len == 3:
+		print ('#ifndef HB_NO_LANGUAGE_LONG')
+	print ('static const LangTag ot_languages%d[] = {' % language_len)
+	for language, tags in sorted (ot.from_bcp_47.items ()):
+		if language == '' or '-' in language:
+			continue
+		if len(language) != language_len: continue
+		commented_out = same_tag (language, tags)
+		for i, tag in enumerate (tags, start=1):
+			print ('%s{%s,\t%s},' % ('/*' if commented_out else '  ', hb_tag (language), hb_tag (tag)), end='')
+			if commented_out:
+				print ('*/', end='')
+			print ('\t/* ', end='')
+			bcp_47_name = bcp_47.names.get (language, '')
+			bcp_47_name_candidates = bcp_47_name.split ('\n')
+			ot_name = ot.names[tag]
+			scope = bcp_47.scopes.get (language, '')
+			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_name) else ot_name, scope))
-		print (' */')
-
-print ('};')
-print ()
+				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 ('};')
+	if language_len == 3:
+		print ('#endif')
+	print ()
 
 print ('/**')
 print (' * hb_ot_tags_from_complex_language:')
@@ -986,19 +991,19 @@ print (' * Converts a multi-subtag BCP 47 language tag to language tags.')
 print (' *')
 print (' * Return value: Whether any language systems were retrieved.')
 print (' **/')
-print ('static bool')
+print ('static inline bool')
 print ('hb_ot_tags_from_complex_language (const char   *lang_str,')
 print ('\t\t\t\t  const char   *limit,')
 print ('\t\t\t\t  unsigned int *count /* IN/OUT */,')
 print ('\t\t\t\t  hb_tag_t     *tags /* OUT */)')
 print ('{')
 
-def print_subtag_matches (subtag, new_line):
+def print_subtag_matches (subtag, string, new_line):
 	if subtag:
 		if new_line:
 			print ()
 			print ('\t&& ', end='')
-		print ('subtag_matches (lang_str, limit, "-%s")' % subtag, end='')
+		print ('subtag_matches (%s, limit, "-%s", %i)' % (string, subtag, 1 + len (subtag)), end='')
 
 complex_tags = collections.defaultdict (list)
 for initial, group in itertools.groupby ((lt_tags for lt_tags in [
@@ -1009,6 +1014,24 @@ for initial, group in itertools.groupby ((lt_tags for lt_tags in [
 		key=lambda lt_tags: lt_tags[0].get_group ()):
 	complex_tags[initial] += group
 
+# Calculate the min length of the subtags outside the switch
+min_subtag_len = 100
+for initial, items in sorted (complex_tags.items ()):
+	if initial != 'und':
+		continue
+	for lt, tags in items:
+		if not tags:
+			continue
+		subtag_len = 0
+		subtag_len += 1 + len (lt.script) if lt.script is not None else 0
+		subtag_len += 1 + len (lt.region) if lt.region is not None else 0
+		subtag_len += 1 + len (lt.variant) if lt.variant is not None else 0
+		min_subtag_len = min(subtag_len, min_subtag_len)
+
+print ('  if (limit - lang_str >= %d)' % (min_subtag_len + 2))
+print ('  {')
+print ("    const char *p = strchr (lang_str, '-');")
+print ("    if (!p || p >= limit || limit - p < %i) goto out;" % min_subtag_len)
 for initial, items in sorted (complex_tags.items ()):
 	if initial != 'und':
 		continue
@@ -1018,29 +1041,31 @@ for initial, items in sorted (complex_tags.items ()):
 		if lt.variant in bcp_47.prefixes:
 			expect (next (iter (bcp_47.prefixes[lt.variant])) == lt.language,
 					'%s is not a valid prefix of %s' % (lt.language, lt.variant))
-		print ('  if (', end='')
-		print_subtag_matches (lt.script, False)
-		print_subtag_matches (lt.region, False)
-		print_subtag_matches (lt.variant, False)
+		print ('    if (', end='')
+		print_subtag_matches (lt.script, 'p', False)
+		print_subtag_matches (lt.region, 'p', False)
+		print_subtag_matches (lt.variant, 'p', False)
 		print (')')
-		print ('  {')
-		write ('    /* %s */' % bcp_47.get_name (lt))
+		print ('    {')
+		write ('      /* %s */' % bcp_47.get_name (lt))
 		print ()
 		if len (tags) == 1:
-			write ('    tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
+			write ('      tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
 			print ()
-			print ('    *count = 1;')
+			print ('      *count = 1;')
 		else:
 			print ('    hb_tag_t possible_tags[] = {')
 			for tag in tags:
 				write ('      %s,  /* %s */' % (hb_tag (tag), ot.names[tag]))
 				print ()
-			print ('    };')
-			print ('    for (i = 0; i < %s && i < *count; i++)' % len (tags))
-			print ('      tags[i] = possible_tags[i];')
-			print ('    *count = i;')
-		print ('    return true;')
-		print ('  }')
+			print ('      };')
+			print ('      for (i = 0; i < %s && i < *count; i++)' % len (tags))
+			print ('\ttags[i] = possible_tags[i];')
+			print ('      *count = i;')
+		print ('      return true;')
+		print ('    }')
+print ('  }')
+print ('out:')
 
 print ('  switch (lang_str[0])')
 print ('  {')
@@ -1067,10 +1092,10 @@ for initial, items in sorted (complex_tags.items ()):
 			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 (script, True)
-		print_subtag_matches (region, True)
-		print_subtag_matches (lt.variant, True)
+				print ('lang_matches (&lang_str[1], limit, "%s", %i)' % (string_literal, len (string_literal)), end='')
+		print_subtag_matches (script, 'lang_str', True)
+		print_subtag_matches (region, 'lang_str', True)
+		print_subtag_matches (lt.variant, 'lang_str', True)
 		print (')')
 		print ('    {')
 		write ('      /* %s */' % bcp_47.get_name (lt))
@@ -1109,7 +1134,7 @@ print (' *')
 print (' * Return value: The #hb_language_t corresponding to the BCP 47 language tag,')
 print (' * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.')
 print (' **/')
-print ('static hb_language_t')
+print ('static inline hb_language_t')
 print ('hb_ot_ambiguous_tag_to_language (hb_tag_t tag)')
 print ('{')
 print ('  switch (tag)')
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py b/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py
index 1c75584473d90ac56b955b2873c97063c1165f86..5daab7063ab50a0af798f070c73347aaa6654002 100755
--- a/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py
@@ -221,7 +221,10 @@ def is_CONS_SUB(U, UISC, UDI, UGC, AJT):
 def is_CONS_WITH_STACKER(U, UISC, UDI, UGC, AJT):
 	return UISC == Consonant_With_Stacker
 def is_HALANT(U, UISC, UDI, UGC, AJT):
-	return UISC == Virama
+	return UISC == Virama and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UDI, UGC, AJT)
+def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UDI, UGC, AJT):
+	# Split off of HALANT
+	return U == 0x0DCA
 def is_HALANT_NUM(U, UISC, UDI, UGC, AJT):
 	return UISC == Number_Joiner
 def is_HIEROGLYPH(U, UISC, UDI, UGC, AJT):
@@ -280,6 +283,7 @@ use_mapping = {
 	'SUB':	is_CONS_SUB,
 	'CS':	is_CONS_WITH_STACKER,
 	'H':	is_HALANT,
+	'HVM':	is_HALANT_OR_VOWEL_MODIFIER,
 	'HN':	is_HALANT_NUM,
 	'IS':	is_INVISIBLE_STACKER,
 	'G':	is_HIEROGLYPH,
@@ -329,6 +333,7 @@ use_positions = {
 		'Blw': [Bottom],
 	},
 	'H': None,
+	'HVM': None,
 	'IS': None,
 	'B': None,
 	'FM': {
@@ -377,6 +382,9 @@ def map_to_use(data):
 		if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top
 		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/issues/3550
+		if U == 0x10A38: UIPC = Bottom
+
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
 		# also  https://github.com/harfbuzz/harfbuzz/issues/1012
 		if 0x1112A <= U <= 0x1112B: UIPC = Top
@@ -409,12 +417,12 @@ for h in headers:
 		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 ("#ifndef HB_OT_SHAPER_USE_TABLE_HH")
+print ("#define HB_OT_SHAPER_USE_TABLE_HH")
 print ()
 print ('#include "hb.hh"')
 print ()
-print ('#include "hb-ot-shape-complex-use-machine.hh"')
+print ('#include "hb-ot-shaper-use-machine.hh"')
 print ()
 
 total = 0
@@ -539,7 +547,7 @@ for k,v in sorted(use_positions.items()):
 		print ("#undef %s" % tag)
 print ()
 print ()
-print ("#endif /* HB_OT_SHAPE_COMPLEX_USE_TABLE_HH */")
+print ("#endif /* HB_OT_SHAPER_USE_TABLE_HH */")
 print ("/* == End of generated table == */")
 
 # Maintain at least 50% occupancy in the table */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/gen-vowel-constraints.py b/source/libs/harfbuzz/harfbuzz-src/src/gen-vowel-constraints.py
index 42afd281ed0f5cefd1f13b83c468f8a29958c917..3c1f6211eb452ede44ccc654315ecaa1cbfc1987 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/gen-vowel-constraints.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/gen-vowel-constraints.py
@@ -5,7 +5,7 @@
 It creates ``_hb_preprocess_text_vowel_constraints``, which inserts dotted
 circles into sequences prohibited by the USE script development spec.
 This function should be used as the ``preprocess_text`` of an
-``hb_ot_complex_shaper_t``.
+``hb_ot_shaper_t``.
 
 usage: ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
 
@@ -166,7 +166,7 @@ print ('#include "hb.hh"')
 print ()
 print ('#ifndef HB_NO_OT_SHAPE')
 print ()
-print ('#include "hb-ot-shape-complex-vowel-constraints.hh"')
+print ('#include "hb-ot-shaper-vowel-constraints.hh"')
 print ()
 print ('static void')
 print ('_output_dotted_circle (hb_buffer_t *buffer)')
@@ -188,7 +188,7 @@ print ('_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB
 print ('\t\t\t\t       hb_buffer_t              *buffer,')
 print ('\t\t\t\t       hb_font_t                *font HB_UNUSED)')
 print ('{')
-print ('#ifdef HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS')
+print ('#ifdef HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS')
 print ('  return;')
 print ('#endif')
 print ('  if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)')
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/graph/graph.hh b/source/libs/harfbuzz/harfbuzz-src/src/graph/graph.hh
new file mode 100644
index 0000000000000000000000000000000000000000..52ca9dd142e238b25271241afdd63f4570fb7497
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/graph/graph.hh
@@ -0,0 +1,860 @@
+/*
+ * Copyright © 2022  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#ifndef GRAPH_GRAPH_HH
+#define GRAPH_GRAPH_HH
+
+namespace graph {
+
+/**
+ * Represents a serialized table in the form of a graph.
+ * Provides methods for modifying and reordering the graph.
+ */
+struct graph_t
+{
+  struct vertex_t
+  {
+    hb_serialize_context_t::object_t obj;
+    int64_t distance = 0 ;
+    int64_t space = 0 ;
+    hb_vector_t<unsigned> parents;
+    unsigned start = 0;
+    unsigned end = 0;
+    unsigned priority = 0;
+
+    friend void swap (vertex_t& a, vertex_t& b)
+    {
+      hb_swap (a.obj, b.obj);
+      hb_swap (a.distance, b.distance);
+      hb_swap (a.space, b.space);
+      hb_swap (a.parents, b.parents);
+      hb_swap (a.start, b.start);
+      hb_swap (a.end, b.end);
+      hb_swap (a.priority, b.priority);
+    }
+
+    bool is_shared () const
+    {
+      return parents.length > 1;
+    }
+
+    unsigned incoming_edges () const
+    {
+      return parents.length;
+    }
+
+    void remove_parent (unsigned parent_index)
+    {
+      for (unsigned i = 0; i < parents.length; i++)
+      {
+        if (parents[i] != parent_index) continue;
+        parents.remove (i);
+        break;
+      }
+    }
+
+    void remap_parents (const hb_vector_t<unsigned>& id_map)
+    {
+      for (unsigned i = 0; i < parents.length; i++)
+        parents[i] = id_map[parents[i]];
+    }
+
+    void remap_parent (unsigned old_index, unsigned new_index)
+    {
+      for (unsigned i = 0; i < parents.length; i++)
+      {
+        if (parents[i] == old_index)
+          parents[i] = new_index;
+      }
+    }
+
+    bool is_leaf () const
+    {
+      return !obj.real_links.length && !obj.virtual_links.length;
+    }
+
+    bool raise_priority ()
+    {
+      if (has_max_priority ()) return false;
+      priority++;
+      return true;
+    }
+
+    bool has_max_priority () const {
+      return priority >= 3;
+    }
+
+    int64_t modified_distance (unsigned order) const
+    {
+      // TODO(garretrieger): once priority is high enough, should try
+      // setting distance = 0 which will force to sort immediately after
+      // it's parent where possible.
+
+      int64_t modified_distance =
+          hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF);
+      if (has_max_priority ()) {
+        modified_distance = 0;
+      }
+      return (modified_distance << 18) | (0x003FFFF & order);
+    }
+
+    int64_t distance_modifier () const
+    {
+      if (!priority) return 0;
+      int64_t table_size = obj.tail - obj.head;
+
+      if (priority == 1)
+        return -table_size / 2;
+
+      return -table_size;
+    }
+  };
+
+  /*
+   * A topological sorting of an object graph. Ordered
+   * in reverse serialization order (first object in the
+   * serialization is at the end of the list). This matches
+   * the 'packed' object stack used internally in the
+   * serializer
+   */
+  template<typename T>
+  graph_t (const T& objects)
+      : parents_invalid (true),
+        distance_invalid (true),
+        positions_invalid (true),
+        successful (true)
+  {
+    num_roots_for_space_.push (1);
+    bool removed_nil = false;
+    vertices_.alloc (objects.length);
+    vertices_scratch_.alloc (objects.length);
+    for (unsigned i = 0; i < objects.length; i++)
+    {
+      // TODO(grieger): check all links point to valid objects.
+
+      // If this graph came from a serialization buffer object 0 is the
+      // nil object. We don't need it for our purposes here so drop it.
+      if (i == 0 && !objects[i])
+      {
+        removed_nil = true;
+        continue;
+      }
+
+      vertex_t* v = vertices_.push ();
+      if (check_success (!vertices_.in_error ()))
+        v->obj = *objects[i];
+      if (!removed_nil) continue;
+      // Fix indices to account for removed nil object.
+      for (auto& l : v->obj.all_links_writer ()) {
+        l.objidx--;
+      }
+    }
+  }
+
+  ~graph_t ()
+  {
+    vertices_.fini ();
+  }
+
+  bool in_error () const
+  {
+    return !successful ||
+        vertices_.in_error () ||
+        num_roots_for_space_.in_error ();
+  }
+
+  const vertex_t& root () const
+  {
+    return vertices_[root_idx ()];
+  }
+
+  unsigned root_idx () const
+  {
+    // Object graphs are in reverse order, the first object is at the end
+    // of the vector. Since the graph is topologically sorted it's safe to
+    // assume the first object has no incoming edges.
+    return vertices_.length - 1;
+  }
+
+  const hb_serialize_context_t::object_t& object(unsigned i) const
+  {
+    return vertices_[i].obj;
+  }
+
+  /*
+   * Generates a new topological sorting of graph ordered by the shortest
+   * distance to each node.
+   */
+  void sort_shortest_distance ()
+  {
+    positions_invalid = true;
+
+    if (vertices_.length <= 1) {
+      // Graph of 1 or less doesn't need sorting.
+      return;
+    }
+
+    update_distances ();
+
+    hb_priority_queue_t queue;
+    hb_vector_t<vertex_t> &sorted_graph = vertices_scratch_;
+    if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return;
+    hb_vector_t<unsigned> id_map;
+    if (unlikely (!check_success (id_map.resize (vertices_.length)))) return;
+
+    hb_vector_t<unsigned> removed_edges;
+    if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return;
+    update_parents ();
+
+    queue.insert (root ().modified_distance (0), root_idx ());
+    int new_id = root_idx ();
+    unsigned order = 1;
+    while (!queue.in_error () && !queue.is_empty ())
+    {
+      unsigned next_id = queue.pop_minimum().second;
+
+      hb_swap (sorted_graph[new_id], vertices_[next_id]);
+      const vertex_t& next = sorted_graph[new_id];
+
+      id_map[next_id] = new_id--;
+
+      for (const auto& link : next.obj.all_links ()) {
+        removed_edges[link.objidx]++;
+        if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx]))
+          // Add the order that the links were encountered to the priority.
+          // This ensures that ties between priorities objects are broken in a consistent
+          // way. More specifically this is set up so that if a set of objects have the same
+          // distance they'll be added to the topological order in the order that they are
+          // referenced from the parent object.
+          queue.insert (vertices_[link.objidx].modified_distance (order++),
+                        link.objidx);
+      }
+    }
+
+    check_success (!queue.in_error ());
+    check_success (!sorted_graph.in_error ());
+    if (!check_success (new_id == -1))
+      print_orphaned_nodes ();
+
+    remap_all_obj_indices (id_map, &sorted_graph);
+
+    hb_swap (vertices_, sorted_graph);
+  }
+
+  /*
+   * Assign unique space numbers to each connected subgraph of 32 bit offset(s).
+   */
+  bool assign_32bit_spaces ()
+  {
+    unsigned root_index = root_idx ();
+    hb_set_t visited;
+    hb_set_t roots;
+    for (unsigned i = 0; i <= root_index; i++)
+    {
+      // Only real links can form 32 bit spaces
+      for (auto& l : vertices_[i].obj.real_links)
+      {
+        if (l.width == 4 && !l.is_signed)
+        {
+          roots.add (l.objidx);
+          find_subgraph (l.objidx, visited);
+        }
+      }
+    }
+
+    // Mark everything not in the subgraphs of 32 bit roots as visited.
+    // This prevents 32 bit subgraphs from being connected via nodes not in the 32 bit subgraphs.
+    visited.invert ();
+
+    if (!roots) return false;
+
+    while (roots)
+    {
+      unsigned next = HB_SET_VALUE_INVALID;
+      if (unlikely (!check_success (!roots.in_error ()))) break;
+      if (!roots.next (&next)) break;
+
+      hb_set_t connected_roots;
+      find_connected_nodes (next, roots, visited, connected_roots);
+      if (unlikely (!check_success (!connected_roots.in_error ()))) break;
+
+      isolate_subgraph (connected_roots);
+      if (unlikely (!check_success (!connected_roots.in_error ()))) break;
+
+      unsigned next_space = this->next_space ();
+      num_roots_for_space_.push (0);
+      for (unsigned root : connected_roots)
+      {
+        DEBUG_MSG (SUBSET_REPACK, nullptr, "Subgraph %u gets space %u", root, next_space);
+        vertices_[root].space = next_space;
+        num_roots_for_space_[next_space] = num_roots_for_space_[next_space] + 1;
+        distance_invalid = true;
+        positions_invalid = true;
+      }
+
+      // TODO(grieger): special case for GSUB/GPOS use extension promotions to move 16 bit space
+      //                into the 32 bit space as needed, instead of using isolation.
+    }
+
+
+
+    return true;
+  }
+
+  /*
+   * Isolates the subgraph of nodes reachable from root. Any links to nodes in the subgraph
+   * that originate from outside of the subgraph will be removed by duplicating the linked to
+   * object.
+   *
+   * Indices stored in roots will be updated if any of the roots are duplicated to new indices.
+   */
+  bool isolate_subgraph (hb_set_t& roots)
+  {
+    update_parents ();
+    hb_map_t subgraph;
+
+    // incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these
+    // set the subgraph incoming edge count to match all of root_idx's incoming edges
+    hb_set_t parents;
+    for (unsigned root_idx : roots)
+    {
+      subgraph.set (root_idx, wide_parents (root_idx, parents));
+      find_subgraph (root_idx, subgraph);
+    }
+
+    unsigned original_root_idx = root_idx ();
+    hb_map_t index_map;
+    bool made_changes = false;
+    for (auto entry : subgraph.iter ())
+    {
+      const auto& node = vertices_[entry.first];
+      unsigned subgraph_incoming_edges = entry.second;
+
+      if (subgraph_incoming_edges < node.incoming_edges ())
+      {
+        // Only  de-dup objects with incoming links from outside the subgraph.
+        made_changes = true;
+        duplicate_subgraph (entry.first, index_map);
+      }
+    }
+
+    if (!made_changes)
+      return false;
+
+    if (original_root_idx != root_idx ()
+        && parents.has (original_root_idx))
+    {
+      // If the root idx has changed since parents was determined, update root idx in parents
+      parents.add (root_idx ());
+      parents.del (original_root_idx);
+    }
+
+    auto new_subgraph =
+        + subgraph.keys ()
+        | hb_map([&] (unsigned node_idx) {
+          const unsigned *v;
+          if (index_map.has (node_idx, &v)) return *v;
+          return node_idx;
+        })
+        ;
+
+    remap_obj_indices (index_map, new_subgraph);
+    remap_obj_indices (index_map, parents.iter (), true);
+
+    // Update roots set with new indices as needed.
+    unsigned next = HB_SET_VALUE_INVALID;
+    while (roots.next (&next))
+    {
+      const unsigned *v;
+      if (index_map.has (next, &v))
+      {
+        roots.del (next);
+        roots.add (*v);
+      }
+    }
+
+    return true;
+  }
+
+  void find_subgraph (unsigned node_idx, hb_map_t& subgraph)
+  {
+    for (const auto& link : vertices_[node_idx].obj.all_links ())
+    {
+      const unsigned *v;
+      if (subgraph.has (link.objidx, &v))
+      {
+        subgraph.set (link.objidx, *v + 1);
+        continue;
+      }
+      subgraph.set (link.objidx, 1);
+      find_subgraph (link.objidx, subgraph);
+    }
+  }
+
+  void find_subgraph (unsigned node_idx, hb_set_t& subgraph)
+  {
+    if (subgraph.has (node_idx)) return;
+    subgraph.add (node_idx);
+    for (const auto& link : vertices_[node_idx].obj.all_links ())
+      find_subgraph (link.objidx, subgraph);
+  }
+
+  /*
+   * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign
+   * links. index_map is updated with mappings from old id to new id. If a duplication has already
+   * been performed for a given index, then it will be skipped.
+   */
+  void duplicate_subgraph (unsigned node_idx, hb_map_t& index_map)
+  {
+    if (index_map.has (node_idx))
+      return;
+
+    index_map.set (node_idx, duplicate (node_idx));
+    for (const auto& l : object (node_idx).all_links ()) {
+      duplicate_subgraph (l.objidx, index_map);
+    }
+  }
+
+  /*
+   * Creates a copy of node_idx and returns it's new index.
+   */
+  unsigned duplicate (unsigned node_idx)
+  {
+    positions_invalid = true;
+    distance_invalid = true;
+
+    auto* clone = vertices_.push ();
+    auto& child = vertices_[node_idx];
+    if (vertices_.in_error ()) {
+      return -1;
+    }
+
+    clone->obj.head = child.obj.head;
+    clone->obj.tail = child.obj.tail;
+    clone->distance = child.distance;
+    clone->space = child.space;
+    clone->parents.reset ();
+
+    unsigned clone_idx = vertices_.length - 2;
+    for (const auto& l : child.obj.real_links)
+    {
+      clone->obj.real_links.push (l);
+      vertices_[l.objidx].parents.push (clone_idx);
+    }
+    for (const auto& l : child.obj.virtual_links)
+    {
+      clone->obj.virtual_links.push (l);
+      vertices_[l.objidx].parents.push (clone_idx);
+    }
+
+    check_success (!clone->obj.real_links.in_error ());
+    check_success (!clone->obj.virtual_links.in_error ());
+
+    // The last object is the root of the graph, so swap back the root to the end.
+    // The root's obj idx does change, however since it's root nothing else refers to it.
+    // all other obj idx's will be unaffected.
+    hb_swap (vertices_[vertices_.length - 2], *clone);
+
+    // Since the root moved, update the parents arrays of all children on the root.
+    for (const auto& l : root ().obj.all_links ())
+      vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ());
+
+    return clone_idx;
+  }
+
+  /*
+   * Creates a copy of child and re-assigns the link from
+   * parent to the clone. The copy is a shallow copy, objects
+   * linked from child are not duplicated.
+   */
+  bool duplicate (unsigned parent_idx, unsigned child_idx)
+  {
+    update_parents ();
+
+    unsigned links_to_child = 0;
+    for (const auto& l : vertices_[parent_idx].obj.all_links ())
+    {
+      if (l.objidx == child_idx) links_to_child++;
+    }
+
+    if (vertices_[child_idx].incoming_edges () <= links_to_child)
+    {
+      // Can't duplicate this node, doing so would orphan the original one as all remaining links
+      // to child are from parent.
+      DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %d => %d",
+                 parent_idx, child_idx);
+      return false;
+    }
+
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %d => %d",
+               parent_idx, child_idx);
+
+    unsigned clone_idx = duplicate (child_idx);
+    if (clone_idx == (unsigned) -1) return false;
+    // duplicate shifts the root node idx, so if parent_idx was root update it.
+    if (parent_idx == clone_idx) parent_idx++;
+
+    auto& parent = vertices_[parent_idx];
+    for (auto& l : parent.obj.all_links_writer ())
+    {
+      if (l.objidx != child_idx)
+        continue;
+
+      reassign_link (l, parent_idx, clone_idx);
+    }
+
+    return true;
+  }
+
+  /*
+   * Raises the sorting priority of all children.
+   */
+  bool raise_childrens_priority (unsigned parent_idx)
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Raising priority of all children of %d",
+               parent_idx);
+    // This operation doesn't change ordering until a sort is run, so no need
+    // to invalidate positions. It does not change graph structure so no need
+    // to update distances or edge counts.
+    auto& parent = vertices_[parent_idx].obj;
+    bool made_change = false;
+    for (auto& l : parent.all_links_writer ())
+      made_change |= vertices_[l.objidx].raise_priority ();
+    return made_change;
+  }
+
+  void print_orphaned_nodes ()
+  {
+    if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
+
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
+    parents_invalid = true;
+    update_parents();
+
+    for (unsigned i = 0; i < root_idx (); i++)
+    {
+      const auto& v = vertices_[i];
+      if (!v.parents)
+        DEBUG_MSG (SUBSET_REPACK, nullptr, "Node %u is orphaned.", i);
+    }
+  }
+
+  unsigned num_roots_for_space (unsigned space) const
+  {
+    return num_roots_for_space_[space];
+  }
+
+  unsigned next_space () const
+  {
+    return num_roots_for_space_.length;
+  }
+
+  void move_to_new_space (const hb_set_t& indices)
+  {
+    num_roots_for_space_.push (0);
+    unsigned new_space = num_roots_for_space_.length - 1;
+
+    for (unsigned index : indices) {
+      auto& node = vertices_[index];
+      num_roots_for_space_[node.space] = num_roots_for_space_[node.space] - 1;
+      num_roots_for_space_[new_space] = num_roots_for_space_[new_space] + 1;
+      node.space = new_space;
+      distance_invalid = true;
+      positions_invalid = true;
+    }
+  }
+
+  unsigned space_for (unsigned index, unsigned* root = nullptr) const
+  {
+    const auto& node = vertices_[index];
+    if (node.space)
+    {
+      if (root != nullptr)
+        *root = index;
+      return node.space;
+    }
+
+    if (!node.parents)
+    {
+      if (root)
+        *root = index;
+      return 0;
+    }
+
+    return space_for (node.parents[0], root);
+  }
+
+  void err_other_error () { this->successful = false; }
+
+  size_t total_size_in_bytes () const {
+    size_t total_size = 0;
+    for (unsigned i = 0; i < vertices_.length; i++) {
+      size_t size = vertices_[i].obj.tail - vertices_[i].obj.head;
+      total_size += size;
+    }
+    return total_size;
+  }
+
+
+ private:
+
+  /*
+   * Returns the numbers of incoming edges that are 32bits wide.
+   */
+  unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const
+  {
+    unsigned count = 0;
+    hb_set_t visited;
+    for (unsigned p : vertices_[node_idx].parents)
+    {
+      if (visited.has (p)) continue;
+      visited.add (p);
+
+      // Only real links can be wide
+      for (const auto& l : vertices_[p].obj.real_links)
+      {
+        if (l.objidx == node_idx && l.width == 4 && !l.is_signed)
+        {
+          count++;
+          parents.add (p);
+        }
+      }
+    }
+    return count;
+  }
+
+  bool check_success (bool success)
+  { return this->successful && (success || ((void) err_other_error (), false)); }
+
+ public:
+  /*
+   * Creates a map from objid to # of incoming edges.
+   */
+  void update_parents ()
+  {
+    if (!parents_invalid) return;
+
+    for (unsigned i = 0; i < vertices_.length; i++)
+      vertices_[i].parents.reset ();
+
+    for (unsigned p = 0; p < vertices_.length; p++)
+    {
+      for (auto& l : vertices_[p].obj.all_links ())
+      {
+        vertices_[l.objidx].parents.push (p);
+      }
+    }
+
+    parents_invalid = false;
+  }
+
+  /*
+   * compute the serialized start and end positions for each vertex.
+   */
+  void update_positions ()
+  {
+    if (!positions_invalid) return;
+
+    unsigned current_pos = 0;
+    for (int i = root_idx (); i >= 0; i--)
+    {
+      auto& v = vertices_[i];
+      v.start = current_pos;
+      current_pos += v.obj.tail - v.obj.head;
+      v.end = current_pos;
+    }
+
+    positions_invalid = false;
+  }
+
+  /*
+   * Finds the distance to each object in the graph
+   * from the initial node.
+   */
+  void update_distances ()
+  {
+    if (!distance_invalid) return;
+
+    // Uses Dijkstra's algorithm to find all of the shortest distances.
+    // https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
+    //
+    // Implementation Note:
+    // Since our priority queue doesn't support fast priority decreases
+    // we instead just add new entries into the queue when a priority changes.
+    // Redundant ones are filtered out later on by the visited set.
+    // According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf
+    // for practical performance this is faster then using a more advanced queue
+    // (such as a fibonacci queue) with a fast decrease priority.
+    for (unsigned i = 0; i < vertices_.length; i++)
+    {
+      if (i == vertices_.length - 1)
+        vertices_[i].distance = 0;
+      else
+        vertices_[i].distance = hb_int_max (int64_t);
+    }
+
+    hb_priority_queue_t queue;
+    queue.insert (0, vertices_.length - 1);
+
+    hb_vector_t<bool> visited;
+    visited.resize (vertices_.length);
+
+    while (!queue.in_error () && !queue.is_empty ())
+    {
+      unsigned next_idx = queue.pop_minimum ().second;
+      if (visited[next_idx]) continue;
+      const auto& next = vertices_[next_idx];
+      int64_t next_distance = vertices_[next_idx].distance;
+      visited[next_idx] = true;
+
+      for (const auto& link : next.obj.all_links ())
+      {
+        if (visited[link.objidx]) continue;
+
+        const auto& child = vertices_[link.objidx].obj;
+        unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide
+        int64_t child_weight = (child.tail - child.head) +
+                               ((int64_t) 1 << (link_width * 8)) * (vertices_[link.objidx].space + 1);
+        int64_t child_distance = next_distance + child_weight;
+
+        if (child_distance < vertices_[link.objidx].distance)
+        {
+          vertices_[link.objidx].distance = child_distance;
+          queue.insert (child_distance, link.objidx);
+        }
+      }
+    }
+
+    check_success (!queue.in_error ());
+    if (!check_success (queue.is_empty ()))
+    {
+      print_orphaned_nodes ();
+      return;
+    }
+
+    distance_invalid = false;
+  }
+
+ private:
+  /*
+   * Updates a link in the graph to point to a different object. Corrects the
+   * parents vector on the previous and new child nodes.
+   */
+  void reassign_link (hb_serialize_context_t::object_t::link_t& link,
+                      unsigned parent_idx,
+                      unsigned new_idx)
+  {
+    unsigned old_idx = link.objidx;
+    link.objidx = new_idx;
+    vertices_[old_idx].remove_parent (parent_idx);
+    vertices_[new_idx].parents.push (parent_idx);
+  }
+
+  /*
+   * Updates all objidx's in all links using the provided mapping. Corrects incoming edge counts.
+   */
+  template<typename Iterator, hb_requires (hb_is_iterator (Iterator))>
+  void remap_obj_indices (const hb_map_t& id_map,
+                          Iterator subgraph,
+                          bool only_wide = false)
+  {
+    if (!id_map) return;
+    for (unsigned i : subgraph)
+    {
+      for (auto& link : vertices_[i].obj.all_links_writer ())
+      {
+        const unsigned *v;
+        if (!id_map.has (link.objidx, &v)) continue;
+        if (only_wide && !(link.width == 4 && !link.is_signed)) continue;
+
+        reassign_link (link, i, *v);
+      }
+    }
+  }
+
+  /*
+   * Updates all objidx's in all links using the provided mapping.
+   */
+  void remap_all_obj_indices (const hb_vector_t<unsigned>& id_map,
+                              hb_vector_t<vertex_t>* sorted_graph) const
+  {
+    for (unsigned i = 0; i < sorted_graph->length; i++)
+    {
+      (*sorted_graph)[i].remap_parents (id_map);
+      for (auto& link : (*sorted_graph)[i].obj.all_links_writer ())
+      {
+        link.objidx = id_map[link.objidx];
+      }
+    }
+  }
+
+  /*
+   * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped.
+   * For this search the graph is treated as being undirected.
+   *
+   * Connected targets will be added to connected and removed from targets. All visited nodes
+   * will be added to visited.
+   */
+  void find_connected_nodes (unsigned start_idx,
+                             hb_set_t& targets,
+                             hb_set_t& visited,
+                             hb_set_t& connected)
+  {
+    if (unlikely (!check_success (!visited.in_error ()))) return;
+    if (visited.has (start_idx)) return;
+    visited.add (start_idx);
+
+    if (targets.has (start_idx))
+    {
+      targets.del (start_idx);
+      connected.add (start_idx);
+    }
+
+    const auto& v = vertices_[start_idx];
+
+    // Graph is treated as undirected so search children and parents of start_idx
+    for (const auto& l : v.obj.all_links ())
+      find_connected_nodes (l.objidx, targets, visited, connected);
+
+    for (unsigned p : v.parents)
+      find_connected_nodes (p, targets, visited, connected);
+  }
+
+ public:
+  // TODO(garretrieger): make private, will need to move most of offset overflow code into graph.
+  hb_vector_t<vertex_t> vertices_;
+  hb_vector_t<vertex_t> vertices_scratch_;
+ private:
+  bool parents_invalid;
+  bool distance_invalid;
+  bool positions_invalid;
+  bool successful;
+  hb_vector_t<unsigned> num_roots_for_space_;
+};
+
+}
+
+#endif  // GRAPH_GRAPH_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/graph/serialize.hh b/source/libs/harfbuzz/harfbuzz-src/src/graph/serialize.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ecc6cc5aea2b579ff68d63c09cb3e8e6a865a741
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/graph/serialize.hh
@@ -0,0 +1,249 @@
+/*
+ * Copyright © 2022  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#ifndef GRAPH_SERIALIZE_HH
+#define GRAPH_SERIALIZE_HH
+
+namespace graph {
+
+struct overflow_record_t
+{
+  unsigned parent;
+  unsigned child;
+};
+
+inline
+int64_t compute_offset (
+    const graph_t& graph,
+    unsigned parent_idx,
+    const hb_serialize_context_t::object_t::link_t& link)
+{
+  const auto& parent = graph.vertices_[parent_idx];
+  const auto& child = graph.vertices_[link.objidx];
+  int64_t offset = 0;
+  switch ((hb_serialize_context_t::whence_t) link.whence) {
+    case hb_serialize_context_t::whence_t::Head:
+      offset = child.start - parent.start; break;
+    case hb_serialize_context_t::whence_t::Tail:
+      offset = child.start - parent.end; break;
+    case hb_serialize_context_t::whence_t::Absolute:
+      offset = child.start; break;
+  }
+
+  assert (offset >= link.bias);
+  offset -= link.bias;
+  return offset;
+}
+
+inline
+bool is_valid_offset (int64_t offset,
+                      const hb_serialize_context_t::object_t::link_t& link)
+{
+  if (unlikely (!link.width))
+    // Virtual links can't overflow.
+    return link.is_signed || offset >= 0;
+
+  if (link.is_signed)
+  {
+    if (link.width == 4)
+      return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
+    else
+      return offset >= -(1 << 15) && offset < (1 << 15);
+  }
+  else
+  {
+    if (link.width == 4)
+      return offset >= 0 && offset < ((int64_t) 1 << 32);
+    else if (link.width == 3)
+      return offset >= 0 && offset < ((int32_t) 1 << 24);
+    else
+      return offset >= 0 && offset < (1 << 16);
+  }
+}
+
+/*
+ * Will any offsets overflow on graph when it's serialized?
+ */
+inline bool
+will_overflow (graph_t& graph,
+               hb_vector_t<overflow_record_t>* overflows = nullptr)
+{
+  if (overflows) overflows->resize (0);
+  graph.update_positions ();
+
+  const auto& vertices = graph.vertices_;
+  for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--)
+  {
+    // Don't need to check virtual links for overflow
+    for (const auto& link : vertices[parent_idx].obj.real_links)
+    {
+      int64_t offset = compute_offset (graph, parent_idx, link);
+      if (is_valid_offset (offset, link))
+        continue;
+
+      if (!overflows) return true;
+
+      overflow_record_t r;
+      r.parent = parent_idx;
+      r.child = link.objidx;
+      overflows->push (r);
+    }
+  }
+
+  if (!overflows) return false;
+  return overflows->length;
+}
+
+inline
+void print_overflows (graph_t& graph,
+                      const hb_vector_t<overflow_record_t>& overflows)
+{
+  if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
+
+  graph.update_parents ();
+  int limit = 10;
+  for (const auto& o : overflows)
+  {
+    if (!limit--) break;
+    const auto& parent = graph.vertices_[o.parent];
+    const auto& child = graph.vertices_[o.child];
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  overflow from "
+               "%4d (%4d in, %4d out, space %2d) => "
+               "%4d (%4d in, %4d out, space %2d)",
+               o.parent,
+               parent.incoming_edges (),
+               parent.obj.real_links.length + parent.obj.virtual_links.length,
+               graph.space_for (o.parent),
+               o.child,
+               child.incoming_edges (),
+               child.obj.real_links.length + child.obj.virtual_links.length,
+               graph.space_for (o.child));
+  }
+  if (overflows.length > 10) {
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  ... plus %d more overflows.", overflows.length - 10);
+  }
+}
+
+template <typename O> inline void
+serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
+                        char* head,
+                        hb_serialize_context_t* c)
+{
+  OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
+  *offset = 0;
+  c->add_link (*offset,
+               // serializer has an extra nil object at the start of the
+               // object array. So all id's are +1 of what our id's are.
+               link.objidx + 1,
+               (hb_serialize_context_t::whence_t) link.whence,
+               link.bias);
+}
+
+inline
+void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
+                     char* head,
+                     hb_serialize_context_t* c)
+{
+  switch (link.width)
+  {
+    case 0:
+      // Virtual links aren't serialized.
+      return;
+    case 4:
+      if (link.is_signed)
+      {
+        serialize_link_of_type<OT::HBINT32> (link, head, c);
+      } else {
+        serialize_link_of_type<OT::HBUINT32> (link, head, c);
+      }
+      return;
+    case 2:
+      if (link.is_signed)
+      {
+        serialize_link_of_type<OT::HBINT16> (link, head, c);
+      } else {
+        serialize_link_of_type<OT::HBUINT16> (link, head, c);
+      }
+      return;
+    case 3:
+      serialize_link_of_type<OT::HBUINT24> (link, head, c);
+      return;
+    default:
+      // Unexpected link width.
+      assert (0);
+  }
+}
+
+/*
+ * serialize graph into the provided serialization buffer.
+ */
+inline hb_blob_t* serialize (const graph_t& graph)
+{
+  hb_vector_t<char> buffer;
+  size_t size = graph.total_size_in_bytes ();
+  if (!buffer.alloc (size)) {
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
+    return nullptr;
+  }
+  hb_serialize_context_t c((void *) buffer, size);
+
+  c.start_serialize<void> ();
+  const auto& vertices = graph.vertices_;
+  for (unsigned i = 0; i < vertices.length; i++) {
+    c.push ();
+
+    size_t size = vertices[i].obj.tail - vertices[i].obj.head;
+    char* start = c.allocate_size <char> (size);
+    if (!start) {
+      DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space.");
+      return nullptr;
+    }
+
+    memcpy (start, vertices[i].obj.head, size);
+
+    // Only real links needs to be serialized.
+    for (const auto& link : vertices[i].obj.real_links)
+      serialize_link (link, start, &c);
+
+    // All duplications are already encoded in the graph, so don't
+    // enable sharing during packing.
+    c.pop_pack (false);
+  }
+  c.end_serialize ();
+
+  if (c.in_error ()) {
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d",
+               c.errors);
+    return nullptr;
+  }
+
+  return c.copy_blob ();
+}
+
+} // namespace graph
+
+#endif // GRAPH_SERIALIZE_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/harfbuzz.cc b/source/libs/harfbuzz/harfbuzz-src/src/harfbuzz.cc
index b6a5957c42575964148b9a5a81ae1391bb40abab..05a864ae11f93e5531e0c914327a6638e2685a04 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/harfbuzz.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/harfbuzz.cc
@@ -22,18 +22,18 @@
 #include "hb-ot-meta.cc"
 #include "hb-ot-metrics.cc"
 #include "hb-ot-name.cc"
-#include "hb-ot-shape-complex-arabic.cc"
-#include "hb-ot-shape-complex-default.cc"
-#include "hb-ot-shape-complex-hangul.cc"
-#include "hb-ot-shape-complex-hebrew.cc"
-#include "hb-ot-shape-complex-indic-table.cc"
-#include "hb-ot-shape-complex-indic.cc"
-#include "hb-ot-shape-complex-khmer.cc"
-#include "hb-ot-shape-complex-myanmar.cc"
-#include "hb-ot-shape-complex-syllabic.cc"
-#include "hb-ot-shape-complex-thai.cc"
-#include "hb-ot-shape-complex-use.cc"
-#include "hb-ot-shape-complex-vowel-constraints.cc"
+#include "hb-ot-shaper-arabic.cc"
+#include "hb-ot-shaper-default.cc"
+#include "hb-ot-shaper-hangul.cc"
+#include "hb-ot-shaper-hebrew.cc"
+#include "hb-ot-shaper-indic-table.cc"
+#include "hb-ot-shaper-indic.cc"
+#include "hb-ot-shaper-khmer.cc"
+#include "hb-ot-shaper-myanmar.cc"
+#include "hb-ot-shaper-syllabic.cc"
+#include "hb-ot-shaper-thai.cc"
+#include "hb-ot-shaper-use.cc"
+#include "hb-ot-shaper-vowel-constraints.cc"
 #include "hb-ot-shape-fallback.cc"
 #include "hb-ot-shape-normalize.cc"
 #include "hb-ot-shape.cc"
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-kerx-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-kerx-table.hh
index 0354b47d5ac6fe177a482698bedfb9b440f57564..6a24c90c3126ea1f299b9189497b61031e0e894b 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-kerx-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-kerx-table.hh
@@ -287,7 +287,7 @@ struct KerxSubTableFormat1
 	       * in the 'kern' table example. */
 	      if (v == -0x8000)
 	      {
-		o.attach_type() = ATTACH_TYPE_NONE;
+		o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
 		o.attach_chain() = 0;
 		o.y_offset = 0;
 	      }
@@ -310,7 +310,7 @@ struct KerxSubTableFormat1
 	      /* CoreText doesn't do crossStream kerning in vertical.  We do. */
 	      if (v == -0x8000)
 	      {
-		o.attach_type() = ATTACH_TYPE_NONE;
+		o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
 		o.attach_chain() = 0;
 		o.x_offset = 0;
 	      }
@@ -567,7 +567,7 @@ struct KerxSubTableFormat4
 	  }
 	  break;
 	}
-	o.attach_type() = ATTACH_TYPE_MARK;
+	o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK;
 	o.attach_chain() = (int) mark - (int) buffer->idx;
 	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
       }
@@ -901,7 +901,7 @@ struct KerxTable
 	unsigned int count = c->buffer->len;
 	for (unsigned int i = 0; i < count; i++)
 	{
-	  pos[i].attach_type() = ATTACH_TYPE_CURSIVE;
+	  pos[i].attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_CURSIVE;
 	  pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1;
 	  /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
 	   * since there needs to be a non-zero attachment for post-positioning to
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-morx-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-morx-table.hh
index b77c1f4d449adbfc1ec52538862e71835c6db8e7..3d053cb13e59247657d8f38b7aaa0487e9173016 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-morx-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-morx-table.hh
@@ -123,7 +123,7 @@ struct RearrangementSubtable
 	bool reverse_l = 3 == (m >> 4);
 	bool reverse_r = 3 == (m & 0x0F);
 
-	if (end - start >= l + r)
+	if (end - start >= l + r && end-start <= HB_MAX_CONTEXT_LENGTH)
 	{
 	  buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len));
 	  buffer->merge_clusters (start, end);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
index c40a55cd1fa6b3ac973684fd44546ba61df9ebe3..f1633d886a741a061a596ac103c95d8ebe3c099d 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
@@ -59,7 +59,7 @@
 	  static inline constexpr T operator | (T l, T r) { return T ((unsigned) l | (unsigned) r); } \
 	  static inline constexpr T operator & (T l, T r) { return T ((unsigned) l & (unsigned) r); } \
 	  static inline constexpr T operator ^ (T l, T r) { return T ((unsigned) l ^ (unsigned) r); } \
-	  static inline constexpr T operator ~ (T r) { return T (~(unsigned int) r); } \
+	  static inline constexpr unsigned operator ~ (T r) { return (~(unsigned) r); } \
 	  static inline T& operator |= (T &l, T r) { l = l | r; return l; } \
 	  static inline T& operator &= (T& l, T r) { l = l & r; return l; } \
 	  static inline T& operator ^= (T& l, T r) { l = l ^ r; return l; } \
@@ -150,10 +150,26 @@ struct BEInt<Type, 4>
 			        uint8_t ((V >> 16) & 0xFF),
 			        uint8_t ((V >>  8) & 0xFF),
 			        uint8_t ((V      ) & 0xFF)} {}
-  constexpr operator Type () const { return (v[0] << 24)
-					  + (v[1] << 16)
-					  + (v[2] <<  8)
-					  + (v[3]      ); }
+
+  struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+  constexpr operator Type () const {
+#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
+    defined(__BYTE_ORDER) && \
+    (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+    /* Spoon-feed the compiler a big-endian integer with alignment 1.
+     * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    return __builtin_bswap32 (((packed_uint32_t *) this)->v);
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+    return ((packed_uint32_t *) this)->v;
+#endif
+#else
+    return (v[0] << 24)
+	 + (v[1] << 16)
+	 + (v[2] <<  8)
+	 + (v[3]      );
+#endif
+  }
   private: uint8_t v[4];
 };
 
@@ -211,31 +227,26 @@ struct
 }
 HB_FUNCOBJ (hb_bool);
 
-template <typename T>
-static inline
-T hb_coerce (const T v) { return v; }
-template <typename T, typename V,
-	  hb_enable_if (!hb_is_same (hb_decay<T>, hb_decay<V>) && std::is_pointer<V>::value)>
-static inline
-T hb_coerce (const V v) { return *v; }
-
 struct
 {
   private:
 
   template <typename T> constexpr auto
-  impl (const T& v, hb_priority<2>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+  impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
 
-  template <typename T> constexpr auto
-  impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
+  template <typename T> constexpr uint32_t
+  impl (const hb::shared_ptr<T>& v, hb_priority<1>) const
+  {
+    return v.get () ? v.get ()->hash () : 0;
+  }
+  template <typename T> constexpr uint32_t
+  impl (const hb::unique_ptr<T>& v, hb_priority<1>) const
+  {
+    return v.get () ? v.get ()->hash () : 0;
+  }
 
-  template <typename T,
-	    hb_enable_if (std::is_integral<T>::value)> constexpr auto
-  impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN
-  (
-    /* Knuth's multiplicative method: */
-    (uint32_t) v * 2654435761u
-  )
+  template <typename T> constexpr auto
+  impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
 
   public:
 
@@ -846,6 +857,11 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
 {
   return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
 }
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3) || hb_in_range (u, lo4, hi4);
+}
 
 
 /*
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
index 1d1476d7cda11439ba6888af0c0484e60f840030..5baeb6f7fef153ef73ccc2d287b9d3af7de788f2 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
@@ -56,7 +56,6 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
   hb_array_t& operator= (const hb_array_t&) = default;
   hb_array_t& operator= (hb_array_t&&) = default;
 
-  constexpr hb_array_t (std::nullptr_t) : hb_array_t () {}
   constexpr hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {}
   template <unsigned int length_>
   constexpr hb_array_t (Type (&array_)[length_]) : hb_array_t (array_, length_) {}
@@ -314,7 +313,6 @@ struct hb_sorted_array_t :
   hb_sorted_array_t& operator= (const hb_sorted_array_t&) = default;
   hb_sorted_array_t& operator= (hb_sorted_array_t&&) = default;
 
-  constexpr hb_sorted_array_t (std::nullptr_t) : hb_sorted_array_t () {}
   constexpr hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
   template <unsigned int length_>
   constexpr hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
@@ -346,7 +344,7 @@ struct hb_sorted_array_t :
     unsigned int i;
     return bfind (x, &i) ? &this->arrayZ[i] : not_found;
   }
-  template <typename T>
+  template <typename T, typename ...Ts>
   const Type *bsearch (const T &x, const Type *not_found = nullptr) const
   {
     unsigned int i;
@@ -384,15 +382,16 @@ struct hb_sorted_array_t :
     }
     return false;
   }
-  template <typename T>
-  bool bsearch_impl (const T &x, unsigned *pos) const
+  template <typename T, typename ...Ts>
+  bool bsearch_impl (const T &x, unsigned *pos, Ts... ds) const
   {
     return hb_bsearch_impl (pos,
 			    x,
 			    this->arrayZ,
 			    this->length,
 			    sizeof (Type),
-			    _hb_cmp_method<T, Type>);
+			    _hb_cmp_method<T, Type, Ts...>,
+			    ds...);
   }
 };
 template <typename T> inline hb_sorted_array_t<T>
@@ -403,7 +402,7 @@ hb_sorted_array (T (&array_)[length_])
 { return hb_sorted_array_t<T> (array_); }
 
 template <typename T>
-bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
+inline bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
 {
   if (o.length != this->length) return false;
   for (unsigned int i = 0; i < this->length; i++) {
@@ -411,8 +410,18 @@ bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
   }
   return true;
 }
-
-/* TODO Specialize operator== for hb_bytes_t and hb_ubytes_t. */
+template <>
+inline bool hb_array_t<const char>::operator == (const hb_array_t<const char> &o) const
+{
+  if (o.length != this->length) return false;
+  return 0 == hb_memcmp (arrayZ, o.arrayZ, length);
+}
+template <>
+inline bool hb_array_t<const unsigned char>::operator == (const hb_array_t<const unsigned char> &o) const
+{
+  if (o.length != this->length) return false;
+  return 0 == hb_memcmp (arrayZ, o.arrayZ, length);
+}
 
 template <>
 inline uint32_t hb_array_t<const char>::hash () const {
@@ -421,7 +430,6 @@ inline uint32_t hb_array_t<const char>::hash () const {
     current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
   return current;
 }
-
 template <>
 inline uint32_t hb_array_t<const unsigned char>::hash () const {
   uint32_t current = 0;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh
index a9e1278de70ba6d26fb1c7b4a1317fb007940f3a..8e8c988716d6826a7c430475246a63a811fd3c5f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh
@@ -39,6 +39,12 @@ struct hb_bimap_t
     back_map.reset ();
   }
 
+  void resize (unsigned pop)
+  {
+    forw_map.resize (pop);
+    back_map.resize (pop);
+  }
+
   bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
 
   void set (hb_codepoint_t lhs, hb_codepoint_t rhs)
@@ -48,17 +54,18 @@ struct hb_bimap_t
     if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; }
 
     forw_map.set (lhs, rhs);
-    if (in_error ()) return;
+    if (unlikely (in_error ())) return;
 
     back_map.set (rhs, lhs);
-    if (in_error ()) forw_map.del (lhs);
+    if (unlikely (in_error ())) forw_map.del (lhs);
   }
 
   hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); }
   hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); }
 
   hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
-  bool has (hb_codepoint_t lhs, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); }
+  bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); }
+
 
   void del (hb_codepoint_t lhs)
   {
@@ -72,7 +79,7 @@ struct hb_bimap_t
     back_map.clear ();
   }
 
-  bool is_empty () const { return get_population () == 0; }
+  bool is_empty () const { return forw_map.is_empty (); }
 
   unsigned int get_population () const { return forw_map.get_population (); }
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh
index 97598366544e34a04d2191a0f15645ffb2d4da60..cbe918ee401a8b27c4d98cb20912cb75fe98f70f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh
@@ -40,11 +40,18 @@ struct hb_bit_page_t
 
   bool is_empty () const
   {
-    for (unsigned int i = 0; i < len (); i++)
+    for (unsigned i = 0; i < len (); i++)
       if (v[i])
 	return false;
     return true;
   }
+  uint32_t hash () const
+  {
+    uint32_t h = 0;
+    for (unsigned i = 0; i < len (); i++)
+      h = h * 31 + hb_hash (v[i]);
+    return h;
+  }
 
   void add (hb_codepoint_t g) { elt (g) |= mask (g); }
   void del (hb_codepoint_t g) { elt (g) &= ~mask (g); }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh
index 4a4ce3405372f81e2094a6ee982a10c555a807a9..27fb0732eabd78a0ca66fd09e20b17383b463080 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh
@@ -38,10 +38,10 @@ struct hb_bit_set_invertible_t
   bool inverted = false;
 
   hb_bit_set_invertible_t () = default;
-  hb_bit_set_invertible_t (hb_bit_set_invertible_t& o) = default;
-  hb_bit_set_invertible_t (hb_bit_set_invertible_t&& o) = default;
+  hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default;
+  hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); }
   hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default;
-  hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& o) = default;
+  hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; }
   friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b)
   {
     if (likely (!a.s.successful || !b.s.successful))
@@ -56,6 +56,7 @@ struct hb_bit_set_invertible_t
   bool in_error () const { return s.in_error (); }
   explicit operator bool () const { return !is_empty (); }
 
+  void alloc (unsigned sz) { s.alloc (sz); }
   void reset ()
   {
     s.reset ();
@@ -79,6 +80,8 @@ struct hb_bit_set_invertible_t
     next (&v);
     return v == INVALID;
   }
+  uint32_t hash () const { return s.hash () ^ (uint32_t) inverted; }
+
   hb_codepoint_t get_min () const
   {
     hb_codepoint_t v = INVALID;
@@ -97,7 +100,7 @@ struct hb_bit_set_invertible_t
 
   void add (hb_codepoint_t g) { unlikely (inverted) ? s.del (g) : s.add (g); }
   bool add_range (hb_codepoint_t a, hb_codepoint_t b)
-  { return unlikely (inverted) ? (s.del_range (a, b), true) : s.add_range (a, b); }
+  { return unlikely (inverted) ? ((void) s.del_range (a, b), true) : s.add_range (a, b); }
 
   template <typename T>
   void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh
index fcaff9f3be304d2c8a1c3139137659416c588d25..438fb66a2cdb41fedd7114325c22dbdc4feee345 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh
@@ -56,7 +56,7 @@ struct hb_bit_set_t
   {
     successful = true;
     population = 0;
-    last_page_lookup = 0;
+    last_page_lookup.set_relaxed (0);
     page_map.init ();
     pages.init ();
   }
@@ -78,7 +78,7 @@ struct hb_bit_set_t
 
   bool successful = true; /* Allocations successful */
   mutable unsigned int population = 0;
-  mutable unsigned int last_page_lookup = 0;
+  mutable hb_atomic_int_t last_page_lookup = 0;
   hb_sorted_vector_t<page_map_t> page_map;
   hb_vector_t<page_t> pages;
 
@@ -97,6 +97,13 @@ struct hb_bit_set_t
     return true;
   }
 
+  void alloc (unsigned sz)
+  {
+    sz >>= (page_t::PAGE_BITS_LOG_2 - 1);
+    pages.alloc (sz);
+    page_map.alloc (sz);
+  }
+
   void reset ()
   {
     successful = true;
@@ -119,6 +126,14 @@ struct hb_bit_set_t
   }
   explicit operator bool () const { return !is_empty (); }
 
+  uint32_t hash () const
+  {
+    uint32_t h = 0;
+    for (auto &map : page_map)
+      h = h * 31 + hb_hash (map.major) + hb_hash (pages[map.index]);
+    return h;
+  }
+
   private:
   void dirty () { population = UINT_MAX; }
   public:
@@ -341,15 +356,14 @@ struct hb_bit_set_t
       return;
     population = other.population;
 
-    /* TODO switch to vector operator =. */
-    hb_memcpy ((void *) pages, (const void *) other.pages, count * pages.item_size);
-    hb_memcpy ((void *) page_map, (const void *) other.page_map, count * page_map.item_size);
+    page_map = other.page_map;
+    pages = other.pages;
   }
 
   bool is_equal (const hb_bit_set_t &other) const
   {
     if (has_population () && other.has_population () &&
-	get_population () != other.get_population ())
+	population != other.population)
       return false;
 
     unsigned int na = pages.length;
@@ -377,7 +391,7 @@ struct hb_bit_set_t
   bool is_subset (const hb_bit_set_t &larger_set) const
   {
     if (has_population () && larger_set.has_population () &&
-	get_population () != larger_set.get_population ())
+	population != larger_set.population)
       return false;
 
     uint32_t spi = 0;
@@ -593,7 +607,7 @@ struct hb_bit_set_t
 
     const auto* page_map_array = page_map.arrayZ;
     unsigned int major = get_major (*codepoint);
-    unsigned int i = last_page_lookup;
+    unsigned int i = last_page_lookup.get_relaxed ();
 
     if (unlikely (i >= page_map.length || page_map_array[i].major != major))
     {
@@ -611,7 +625,7 @@ struct hb_bit_set_t
       if (pages_array[current.index].next (codepoint))
       {
         *codepoint += current.major * page_t::PAGE_BITS;
-        last_page_lookup = i;
+        last_page_lookup.set_relaxed (i);
         return true;
       }
       i++;
@@ -624,11 +638,11 @@ struct hb_bit_set_t
       if (m != INVALID)
       {
 	*codepoint = current.major * page_t::PAGE_BITS + m;
-        last_page_lookup = i;
+        last_page_lookup.set_relaxed (i);
 	return true;
       }
     }
-    last_page_lookup = 0;
+    last_page_lookup.set_relaxed (0);
     *codepoint = INVALID;
     return false;
   }
@@ -711,7 +725,7 @@ struct hb_bit_set_t
     {
       const auto* page_map_array = page_map.arrayZ;
       unsigned int major = get_major (codepoint);
-      unsigned int i = last_page_lookup;
+      unsigned int i = last_page_lookup.get_relaxed ();
       if (unlikely (i >= page_map.length || page_map_array[i].major != major))
       {
 	page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST);
@@ -752,7 +766,7 @@ struct hb_bit_set_t
     {
       const auto* page_map_array = page_map.arrayZ;
       unsigned int major = get_major (codepoint);
-      unsigned int i = last_page_lookup;
+      unsigned int i = last_page_lookup.get_relaxed ();
       if (unlikely (i >= page_map.length || page_map_array[i].major != major))
       {
         page_map.bfind(major, &i, HB_NOT_FOUND_STORE_CLOSEST);
@@ -874,8 +888,20 @@ struct hb_bit_set_t
 
   page_t *page_for (hb_codepoint_t g, bool insert = false)
   {
-    page_map_t map = {get_major (g), pages.length};
-    unsigned int i;
+    unsigned major = get_major (g);
+
+    /* The extra page_map length is necessary; can't just rely on vector here,
+     * since the next check would be tricked because a null page also has
+     * major==0, which we can't distinguish from an actualy major==0 page... */
+    unsigned i = last_page_lookup.get_relaxed ();
+    if (likely (i < page_map.length))
+    {
+      auto &cached_page = page_map.arrayZ[i];
+      if (cached_page.major == major)
+	return &pages[cached_page.index];
+    }
+
+    page_map_t map = {major, pages.length};
     if (!page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST))
     {
       if (!insert)
@@ -890,15 +916,31 @@ struct hb_bit_set_t
 	       (page_map.length - 1 - i) * page_map.item_size);
       page_map[i] = map;
     }
+
+    last_page_lookup.set_relaxed (i);
     return &pages[page_map[i].index];
   }
   const page_t *page_for (hb_codepoint_t g) const
   {
-    page_map_t key = {get_major (g)};
-    const page_map_t *found = page_map.bsearch (key);
-    if (found)
-      return &pages[found->index];
-    return nullptr;
+    unsigned major = get_major (g);
+
+    /* The extra page_map length is necessary; can't just rely on vector here,
+     * since the next check would be tricked because a null page also has
+     * major==0, which we can't distinguish from an actualy major==0 page... */
+    unsigned i = last_page_lookup.get_relaxed ();
+    if (likely (i < page_map.length))
+    {
+      auto &cached_page = page_map.arrayZ[i];
+      if (cached_page.major == major)
+	return &pages[cached_page.index];
+    }
+
+    page_map_t key = {major};
+    if (!page_map.bfind (key, &i))
+      return nullptr;
+
+    last_page_lookup.set_relaxed (i);
+    return &pages[page_map[i].index];
   }
   page_t &page_at (unsigned int i) { return pages[page_map[i].index]; }
   const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-blob.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-blob.cc
index 65e44c7f6a779fcad3af2a371a2b44bb211cb5fc..b561a9374e37bb1a0c5ea09b432d0ca6c4ce02f3 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-blob.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-blob.cc
@@ -369,7 +369,7 @@ hb_blob_get_length (hb_blob_t *blob)
  *
  * Fetches the data from a blob.
  *
- * Returns: (transfer none) (array length=length): the byte data of @blob.
+ * Returns: (nullable) (transfer none) (array length=length): the byte data of @blob.
  *
  * Since: 0.9.2
  **/
@@ -572,7 +572,7 @@ _open_resource_fork (const char *file_name, hb_mapped_file_t *file)
 
   strncpy (rsrc_name, file_name, name_len);
   strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC,
-	   sizeof (_PATH_RSRCFORKSPEC) - 1);
+	   sizeof (_PATH_RSRCFORKSPEC));
 
   int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0);
   hb_free (rsrc_name);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-serialize.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-serialize.cc
index 6539b89640a142016904681849214480b950e97b..3f619a113e8e0b28966e8fbc412861a9a223e082 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-serialize.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-serialize.cc
@@ -31,7 +31,7 @@
 #include "hb-buffer.hh"
 
 
-static const char *serialize_formats[] = {
+static const char *_hb_buffer_serialize_formats[] = {
   "text",
   "json",
   nullptr
@@ -50,7 +50,7 @@ static const char *serialize_formats[] = {
 const char **
 hb_buffer_serialize_list_formats ()
 {
-  return serialize_formats;
+  return _hb_buffer_serialize_formats;
 }
 
 /**
@@ -91,8 +91,8 @@ hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
 {
   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 _hb_buffer_serialize_formats[0];
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_serialize_formats[1];
     default:
     case HB_BUFFER_SERIALIZE_FORMAT_INVALID:  return nullptr;
   }
@@ -400,9 +400,9 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
  * @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.
+ * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes 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.
+ *        read glyph names and extents. If %NULL, an 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.
@@ -514,7 +514,7 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
  * @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.
+ * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes 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.
@@ -637,9 +637,9 @@ _hb_buffer_serialize_invalid (hb_buffer_t *buffer,
  * @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.
+ * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes 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.
+ *        read glyph names and extents. If %NULL, an 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.
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-verify.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-verify.cc
index dea2c11c35eb2d8d6e8ef26e38e1903d95f13b2f..5453e1ca94ee43d0472d4af85c0e86b5b76791ee 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-verify.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-verify.cc
@@ -102,9 +102,9 @@ buffer_verify_unsafe_to_break (hb_buffer_t  *buffer,
   /* Check that breaking up shaping at safe-to-break is indeed safe. */
 
   hb_buffer_t *fragment = hb_buffer_create_similar (buffer);
-  hb_buffer_set_flags (fragment, hb_buffer_get_flags (fragment) & ~HB_BUFFER_FLAG_VERIFY);
+  hb_buffer_set_flags (fragment, (hb_buffer_flags_t (hb_buffer_get_flags (fragment) & ~HB_BUFFER_FLAG_VERIFY)));
   hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer);
-  hb_buffer_set_flags (reconstruction, hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY);
+  hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY)));
 
   unsigned int num_glyphs;
   hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs);
@@ -169,6 +169,12 @@ buffer_verify_unsafe_to_break (hb_buffer_t  *buffer,
       hb_buffer_destroy (fragment);
       return false;
     }
+    else if (!fragment->successful || fragment->shaping_failed)
+    {
+      hb_buffer_destroy (reconstruction);
+      hb_buffer_destroy (fragment);
+      return true;
+    }
     hb_buffer_append (reconstruction, fragment, 0, -1);
 
     start = end;
@@ -238,10 +244,10 @@ buffer_verify_unsafe_to_concat (hb_buffer_t        *buffer,
 
   hb_buffer_t *fragments[2] {hb_buffer_create_similar (buffer),
 			     hb_buffer_create_similar (buffer)};
-  hb_buffer_set_flags (fragments[0], hb_buffer_get_flags (fragments[0]) & ~HB_BUFFER_FLAG_VERIFY);
-  hb_buffer_set_flags (fragments[1], hb_buffer_get_flags (fragments[1]) & ~HB_BUFFER_FLAG_VERIFY);
+  hb_buffer_set_flags (fragments[0], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[0]) & ~HB_BUFFER_FLAG_VERIFY)));
+  hb_buffer_set_flags (fragments[1], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[1]) & ~HB_BUFFER_FLAG_VERIFY)));
   hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer);
-  hb_buffer_set_flags (reconstruction, hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY);
+  hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY)));
   hb_segment_properties_t props;
   hb_buffer_get_segment_properties (buffer, &props);
   hb_buffer_set_segment_properties (fragments[0], &props);
@@ -317,12 +323,22 @@ buffer_verify_unsafe_to_concat (hb_buffer_t        *buffer,
     ret = false;
     goto out;
   }
+  else if (!fragments[0]->successful || fragments[0]->shaping_failed)
+  {
+    ret = true;
+    goto out;
+  }
   if (!hb_shape_full (font, fragments[1], features, num_features, shapers))
   {
     buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment.");
     ret = false;
     goto out;
   }
+  else if (!fragments[1]->successful || fragments[1]->shaping_failed)
+  {
+    ret = true;
+    goto out;
+  }
 
   if (!forward)
   {
@@ -402,6 +418,7 @@ hb_buffer_t::verify (hb_buffer_t        *text_buffer,
     ret = false;
   if (!ret)
   {
+#ifndef HB_NO_BUFFER_SERIALIZE
     unsigned len = text_buffer->len;
     hb_vector_t<char> bytes;
     if (likely (bytes.resize (len * 10 + 16)))
@@ -414,6 +431,7 @@ hb_buffer_t::verify (hb_buffer_t        *text_buffer,
 				   HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS);
       buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ);
     }
+#endif
   }
   return ret;
 }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc
index 6a9ee3ccc8b49a947793f9a962a3efb5268634c4..2272ecf09b51abbf7cf62273f614c318c8cda2e3 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc
@@ -81,8 +81,8 @@ hb_segment_properties_equal (const hb_segment_properties_t *a,
 unsigned int
 hb_segment_properties_hash (const hb_segment_properties_t *p)
 {
-  return (unsigned int) p->direction ^
-	 (unsigned int) p->script ^
+  return ((unsigned int) p->direction * 31 +
+	  (unsigned int) p->script) * 31 +
 	 (intptr_t) (p->language);
 }
 
@@ -289,6 +289,7 @@ hb_buffer_t::clear ()
   props = default_props;
 
   successful = true;
+  shaping_failed = false;
   have_output = false;
   have_positions = false;
 
@@ -310,6 +311,7 @@ hb_buffer_t::enter ()
 {
   deallocate_var_all ();
   serial = 0;
+  shaping_failed = false;
   scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
   if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR)))
   {
@@ -329,6 +331,7 @@ hb_buffer_t::leave ()
   max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
   deallocate_var_all ();
   serial = 0;
+  // Intentionally not reseting shaping_failed, such that it can be inspected.
 }
 
 
@@ -542,7 +545,8 @@ hb_buffer_t::delete_glyph ()
   /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */
 
   unsigned int cluster = info[idx].cluster;
-  if (idx + 1 < len && cluster == info[idx + 1].cluster)
+  if ((idx + 1 < len && cluster == info[idx + 1].cluster) ||
+      (out_len && cluster == out_info[out_len - 1].cluster))
   {
     /* Cluster survives; do nothing. */
     goto done;
@@ -623,6 +627,7 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) =
   HB_SEGMENT_PROPERTIES_DEFAULT,
 
   false, /* successful */
+  true, /* shaping_failed */
   false, /* have_output */
   true  /* have_positions */
 
@@ -631,7 +636,7 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) =
 
 
 /**
- * hb_buffer_create: (Xconstructor)
+ * hb_buffer_create:
  *
  * Creates a new #hb_buffer_t with all properties to defaults.
  *
@@ -834,7 +839,7 @@ hb_buffer_set_content_type (hb_buffer_t              *buffer,
  * Since: 0.9.5
  **/
 hb_buffer_content_type_t
-hb_buffer_get_content_type (hb_buffer_t *buffer)
+hb_buffer_get_content_type (const hb_buffer_t *buffer)
 {
   return buffer->content_type;
 }
@@ -876,7 +881,7 @@ hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
  * Since: 0.9.2
  **/
 hb_unicode_funcs_t *
-hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
+hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer)
 {
   return buffer->unicode;
 }
@@ -919,7 +924,7 @@ hb_buffer_set_direction (hb_buffer_t    *buffer,
  * Since: 0.9.2
  **/
 hb_direction_t
-hb_buffer_get_direction (hb_buffer_t    *buffer)
+hb_buffer_get_direction (const hb_buffer_t *buffer)
 {
   return buffer->props.direction;
 }
@@ -963,7 +968,7 @@ hb_buffer_set_script (hb_buffer_t *buffer,
  * Since: 0.9.2
  **/
 hb_script_t
-hb_buffer_get_script (hb_buffer_t *buffer)
+hb_buffer_get_script (const hb_buffer_t *buffer)
 {
   return buffer->props.script;
 }
@@ -1007,7 +1012,7 @@ hb_buffer_set_language (hb_buffer_t   *buffer,
  * Since: 0.9.2
  **/
 hb_language_t
-hb_buffer_get_language (hb_buffer_t *buffer)
+hb_buffer_get_language (const hb_buffer_t *buffer)
 {
   return buffer->props.language;
 }
@@ -1043,7 +1048,7 @@ hb_buffer_set_segment_properties (hb_buffer_t *buffer,
  * Since: 0.9.7
  **/
 void
-hb_buffer_get_segment_properties (hb_buffer_t *buffer,
+hb_buffer_get_segment_properties (const hb_buffer_t *buffer,
 				  hb_segment_properties_t *props)
 {
   *props = buffer->props;
@@ -1081,7 +1086,7 @@ hb_buffer_set_flags (hb_buffer_t       *buffer,
  * Since: 0.9.7
  **/
 hb_buffer_flags_t
-hb_buffer_get_flags (hb_buffer_t *buffer)
+hb_buffer_get_flags (const hb_buffer_t *buffer)
 {
   return buffer->flags;
 }
@@ -1120,7 +1125,7 @@ hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
  * Since: 0.9.42
  **/
 hb_buffer_cluster_level_t
-hb_buffer_get_cluster_level (hb_buffer_t *buffer)
+hb_buffer_get_cluster_level (const hb_buffer_t *buffer)
 {
   return buffer->cluster_level;
 }
@@ -1161,7 +1166,7 @@ hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
  * Since: 0.9.31
  **/
 hb_codepoint_t
-hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer)
+hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer)
 {
   return buffer->replacement;
 }
@@ -1201,7 +1206,7 @@ hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
  * Since: 2.0.0
  **/
 hb_codepoint_t
-hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer)
+hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer)
 {
   return buffer->invisible;
 }
@@ -1241,7 +1246,7 @@ hb_buffer_set_not_found_glyph (hb_buffer_t    *buffer,
  * Since: 3.1.0
  **/
 hb_codepoint_t
-hb_buffer_get_not_found_glyph (hb_buffer_t    *buffer)
+hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer)
 {
   return buffer->not_found;
 }
@@ -1381,7 +1386,7 @@ hb_buffer_set_length (hb_buffer_t  *buffer,
  * Since: 0.9.2
  **/
 unsigned int
-hb_buffer_get_length (hb_buffer_t *buffer)
+hb_buffer_get_length (const hb_buffer_t *buffer)
 {
   return buffer->len;
 }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.h
index 14aaa5e1f570eaa70c4734db5cc4292528a909a3..22fb3496f2ec7cbebab87bcd26b0100e73310ac8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.h
@@ -289,7 +289,7 @@ hb_buffer_set_content_type (hb_buffer_t              *buffer,
 			    hb_buffer_content_type_t  content_type);
 
 HB_EXTERN hb_buffer_content_type_t
-hb_buffer_get_content_type (hb_buffer_t *buffer);
+hb_buffer_get_content_type (const hb_buffer_t *buffer);
 
 
 HB_EXTERN void
@@ -297,21 +297,21 @@ hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode_funcs);
 
 HB_EXTERN hb_unicode_funcs_t *
-hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer);
+hb_buffer_get_unicode_funcs (const hb_buffer_t  *buffer);
 
 HB_EXTERN void
 hb_buffer_set_direction (hb_buffer_t    *buffer,
 			 hb_direction_t  direction);
 
 HB_EXTERN hb_direction_t
-hb_buffer_get_direction (hb_buffer_t *buffer);
+hb_buffer_get_direction (const hb_buffer_t *buffer);
 
 HB_EXTERN void
 hb_buffer_set_script (hb_buffer_t *buffer,
 		      hb_script_t  script);
 
 HB_EXTERN hb_script_t
-hb_buffer_get_script (hb_buffer_t *buffer);
+hb_buffer_get_script (const hb_buffer_t *buffer);
 
 HB_EXTERN void
 hb_buffer_set_language (hb_buffer_t   *buffer,
@@ -319,14 +319,14 @@ hb_buffer_set_language (hb_buffer_t   *buffer,
 
 
 HB_EXTERN hb_language_t
-hb_buffer_get_language (hb_buffer_t *buffer);
+hb_buffer_get_language (const hb_buffer_t *buffer);
 
 HB_EXTERN void
 hb_buffer_set_segment_properties (hb_buffer_t *buffer,
 				  const hb_segment_properties_t *props);
 
 HB_EXTERN void
-hb_buffer_get_segment_properties (hb_buffer_t *buffer,
+hb_buffer_get_segment_properties (const hb_buffer_t *buffer,
 				  hb_segment_properties_t *props);
 
 HB_EXTERN void
@@ -373,6 +373,7 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer);
  *                      flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT
  *                      glyph-flag should be produced by the shaper. By default
  *                      it will not be produced since it incurs a cost. Since: 4.0.0
+ * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0
  *
  * Flags for #hb_buffer_t.
  *
@@ -386,7 +387,9 @@ typedef enum { /*< flags >*/
   HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES	= 0x00000008u,
   HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE	= 0x00000010u,
   HB_BUFFER_FLAG_VERIFY				= 0x00000020u,
-  HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT	= 0x00000040u
+  HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT	= 0x00000040u,
+
+  HB_BUFFER_FLAG_DEFINED			= 0x0000007Fu
 } hb_buffer_flags_t;
 
 HB_EXTERN void
@@ -394,7 +397,7 @@ hb_buffer_set_flags (hb_buffer_t       *buffer,
 		     hb_buffer_flags_t  flags);
 
 HB_EXTERN hb_buffer_flags_t
-hb_buffer_get_flags (hb_buffer_t *buffer);
+hb_buffer_get_flags (const hb_buffer_t *buffer);
 
 /**
  * hb_buffer_cluster_level_t:
@@ -436,7 +439,7 @@ hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
 			     hb_buffer_cluster_level_t  cluster_level);
 
 HB_EXTERN hb_buffer_cluster_level_t
-hb_buffer_get_cluster_level (hb_buffer_t *buffer);
+hb_buffer_get_cluster_level (const hb_buffer_t *buffer);
 
 /**
  * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT:
@@ -453,21 +456,21 @@ hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
 				     hb_codepoint_t  replacement);
 
 HB_EXTERN hb_codepoint_t
-hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer);
+hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer);
 
 HB_EXTERN void
 hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
 			       hb_codepoint_t  invisible);
 
 HB_EXTERN hb_codepoint_t
-hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer);
+hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer);
 
 HB_EXTERN void
 hb_buffer_set_not_found_glyph (hb_buffer_t    *buffer,
 			       hb_codepoint_t  not_found);
 
 HB_EXTERN hb_codepoint_t
-hb_buffer_get_not_found_glyph (hb_buffer_t    *buffer);
+hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer);
 
 
 /*
@@ -549,7 +552,7 @@ hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length);
 
 HB_EXTERN unsigned int
-hb_buffer_get_length (hb_buffer_t *buffer);
+hb_buffer_get_length (const hb_buffer_t *buffer);
 
 /* Getting glyphs out of the buffer */
 
@@ -583,6 +586,7 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
  * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0
  * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances,
  *  glyph offsets will reflect absolute glyph positions. Since: 1.8.0
+ * @HB_BUFFER_SERIALIZE_FLAG_DEFINED: All currently defined flags. Since: 4.4.0
  *
  * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs().
  *
@@ -595,7 +599,9 @@ typedef enum { /*< flags >*/
   HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES	= 0x00000004u,
   HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u,
   HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS		= 0x00000010u,
-  HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES		= 0x00000020u
+  HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES		= 0x00000020u,
+
+  HB_BUFFER_SERIALIZE_FLAG_DEFINED		= 0x0000003Fu
 } hb_buffer_serialize_flags_t;
 
 /**
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh
index bc6992905e4be1d1dd7e95afd8a4727f0e6d9b09..6ca78f28d451cded00030ef5ab271b5e42020f99 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh
@@ -57,6 +57,7 @@
 static_assert ((sizeof (hb_glyph_info_t) == 20), "");
 static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), "");
 
+HB_MARK_AS_FLAG_T (hb_glyph_flags_t);
 HB_MARK_AS_FLAG_T (hb_buffer_flags_t);
 HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t);
 HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t);
@@ -69,12 +70,13 @@ enum hb_buffer_scratch_flags_t {
   HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT		= 0x00000008u,
   HB_BUFFER_SCRATCH_FLAG_HAS_CGJ			= 0x00000010u,
   HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS		= 0x00000020u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE		= 0x00000040u,
 
-  /* Reserved for complex shapers' internal use. */
-  HB_BUFFER_SCRATCH_FLAG_COMPLEX0			= 0x01000000u,
-  HB_BUFFER_SCRATCH_FLAG_COMPLEX1			= 0x02000000u,
-  HB_BUFFER_SCRATCH_FLAG_COMPLEX2			= 0x04000000u,
-  HB_BUFFER_SCRATCH_FLAG_COMPLEX3			= 0x08000000u,
+  /* Reserved for shapers' internal use. */
+  HB_BUFFER_SCRATCH_FLAG_SHAPER0			= 0x01000000u,
+  HB_BUFFER_SCRATCH_FLAG_SHAPER1			= 0x02000000u,
+  HB_BUFFER_SCRATCH_FLAG_SHAPER2			= 0x04000000u,
+  HB_BUFFER_SCRATCH_FLAG_SHAPER3			= 0x08000000u,
 };
 HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t);
 
@@ -106,6 +108,7 @@ struct hb_buffer_t
   hb_segment_properties_t props; /* Script, language, direction */
 
   bool successful; /* Allocations successful */
+  bool shaping_failed; /* Shaping failure */
   bool have_output; /* Whether we have an output buffer going on */
   bool have_positions; /* Whether we have positions */
 
@@ -130,9 +133,7 @@ struct hb_buffer_t
    * Managed by enter / leave
    */
 
-#ifndef HB_NDEBUG
   uint8_t allocated_var_bits;
-#endif
   uint8_t serial;
   hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
   unsigned int max_len; /* Maximum allowed len. */
@@ -161,38 +162,40 @@ struct hb_buffer_t
 
   void allocate_var (unsigned int start, unsigned int count)
   {
-#ifndef HB_NDEBUG
     unsigned int end = start + count;
     assert (end <= 8);
     unsigned int bits = (1u<<end) - (1u<<start);
     assert (0 == (allocated_var_bits & bits));
     allocated_var_bits |= bits;
-#endif
+  }
+  bool try_allocate_var (unsigned int start, unsigned int count)
+  {
+    unsigned int end = start + count;
+    assert (end <= 8);
+    unsigned int bits = (1u<<end) - (1u<<start);
+    if (allocated_var_bits & bits)
+      return false;
+    allocated_var_bits |= bits;
+    return true;
   }
   void deallocate_var (unsigned int start, unsigned int count)
   {
-#ifndef HB_NDEBUG
     unsigned int end = start + count;
     assert (end <= 8);
     unsigned int bits = (1u<<end) - (1u<<start);
     assert (bits == (allocated_var_bits & bits));
     allocated_var_bits &= ~bits;
-#endif
   }
   void assert_var (unsigned int start, unsigned int count)
   {
-#ifndef HB_NDEBUG
     unsigned int end = start + count;
     assert (end <= 8);
-    unsigned int bits = (1u<<end) - (1u<<start);
+    HB_UNUSED unsigned int bits = (1u<<end) - (1u<<start);
     assert (bits == (allocated_var_bits & bits));
-#endif
   }
   void deallocate_var_all ()
   {
-#ifndef HB_NDEBUG
     allocated_var_bits = 0;
-#endif
   }
 
   hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
@@ -549,7 +552,7 @@ struct hb_buffer_t
 #ifdef HB_NO_BUFFER_MESSAGE
    return true;
 #else
-    if (!messaging ())
+    if (likely (!messaging ()))
       return true;
 
     message_depth++;
@@ -619,9 +622,10 @@ DECLARE_NULL_INSTANCE (hb_buffer_t);
 #define HB_BUFFER_XALLOCATE_VAR(b, func, var) \
   b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
 	   sizeof (b->info[0].var))
-#define HB_BUFFER_ALLOCATE_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, allocate_var,   var ())
-#define HB_BUFFER_DEALLOCATE_VAR(b, var)	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ())
-#define HB_BUFFER_ASSERT_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, assert_var,     var ())
+#define HB_BUFFER_ALLOCATE_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, allocate_var,     var ())
+#define HB_BUFFER_TRY_ALLOCATE_VAR(b, var)	HB_BUFFER_XALLOCATE_VAR (b, try_allocate_var, var ())
+#define HB_BUFFER_DEALLOCATE_VAR(b, var)	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var,   var ())
+#define HB_BUFFER_ASSERT_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, assert_var,       var ())
 
 
 #endif /* HB_BUFFER_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-cache.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-cache.hh
index e617b75de90a7de41eec06ab1dde77b5aa5b29f5..d6b229ed65301b8b81a5c9d667e81607c1690298 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-cache.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-cache.hh
@@ -32,7 +32,7 @@
 
 /* Implements a lockfree cache for int->int functions. */
 
-template <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
+template <unsigned int key_bits=16, unsigned int value_bits=8 + 32 - key_bits, unsigned int cache_bits=8>
 struct hb_cache_t
 {
   static_assert ((key_bits >= cache_bits), "");
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh
index 641de0eff2b507aaddb94ec010268158d3ba86a5..5c2cb060a412bdd58476dbad0efc0083f50c2a90 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh
@@ -248,6 +248,9 @@ struct number_t
 /* byte string */
 struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
 {
+  hb_ubytes_t as_ubytes (unsigned l) const
+  { return hb_ubytes_t ((const unsigned char *) this, l); }
+
   // encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
   template <typename T, typename V>
   static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value)
@@ -274,33 +277,10 @@ struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
   /* Defining null_size allows a Null object may be created. Should be safe because:
    * A descendent struct Dict uses a Null pointer to indicate a missing table,
    * checked before access.
-   * byte_str_t, a wrapper struct pairing a byte pointer along with its length, always
-   * checks the length before access. A Null pointer is used as the initial pointer
-   * along with zero length by the default ctor.
    */
   DEFINE_SIZE_MIN(0);
 };
 
-/* Holder of a section of byte string within a CFFIndex entry */
-struct byte_str_t : hb_ubytes_t
-{
-  byte_str_t ()
-    : hb_ubytes_t () {}
-  byte_str_t (const UnsizedByteStr& s, unsigned int l)
-    : hb_ubytes_t ((const unsigned char*)&s, l) {}
-  byte_str_t (const unsigned char *s, unsigned int l)
-    : hb_ubytes_t (s, l) {}
-  byte_str_t (const hb_ubytes_t &ub)	/* conversion from hb_ubytes_t */
-    : hb_ubytes_t (ub) {}
-
-  /* sub-string */
-  byte_str_t sub_str (unsigned int offset, unsigned int len_) const
-  { return byte_str_t (hb_ubytes_t::sub_array (offset, len_)); }
-
-  bool check_limit (unsigned int offset, unsigned int count) const
-  { return (offset + count <= length); }
-};
-
 /* A byte string associated with the current offset and an error condition */
 struct byte_str_ref_t
 {
@@ -308,17 +288,17 @@ struct byte_str_ref_t
 
   void init ()
   {
-    str = byte_str_t ();
+    str = hb_ubytes_t ();
     offset = 0;
     error = false;
   }
 
   void fini () {}
 
-  byte_str_ref_t (const byte_str_t &str_, unsigned int offset_ = 0)
+  byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0)
     : str (str_), offset (offset_), error (false) {}
 
-  void reset (const byte_str_t &str_, unsigned int offset_ = 0)
+  void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0)
   {
     str = str_;
     offset = offset_;
@@ -334,14 +314,14 @@ struct byte_str_ref_t
     return str[offset + i];
   }
 
-  /* Conversion to byte_str_t */
-  operator byte_str_t () const { return str.sub_str (offset, str.length - offset); }
+  /* Conversion to hb_ubytes_t */
+  operator hb_ubytes_t () const { return str.sub_array (offset, str.length - offset); }
 
-  byte_str_t sub_str (unsigned int offset_, unsigned int len_) const
-  { return str.sub_str (offset_, len_); }
+  hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const
+  { return str.sub_array (offset_, len_); }
 
   bool avail (unsigned int count=1) const
-  { return (!in_error () && str.check_limit (offset, count)); }
+  { return (!in_error () && offset + count <= str.length); }
   void inc (unsigned int count=1)
   {
     if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length)))
@@ -358,44 +338,39 @@ struct byte_str_ref_t
   void set_error ()      { error = true; }
   bool in_error () const { return error; }
 
-  byte_str_t       str;
+  hb_ubytes_t       str;
   unsigned int  offset; /* beginning of the sub-string within str */
 
   protected:
   bool	  error;
 };
 
-typedef hb_vector_t<byte_str_t> byte_str_array_t;
+using byte_str_array_t = hb_vector_t<hb_ubytes_t>;
 
 /* stack */
 template <typename ELEM, int LIMIT>
 struct cff_stack_t
 {
-  void init ()
-  {
-    error = false;
-    count = 0;
-    elements.init ();
-    elements.resize (kSizeLimit);
-  }
-  void fini () { elements.fini (); }
-
   ELEM& operator [] (unsigned int i)
   {
-    if (unlikely (i >= count)) set_error ();
+    if (unlikely (i >= count))
+    {
+      set_error ();
+      return Crap (ELEM);
+    }
     return elements[i];
   }
 
   void push (const ELEM &v)
   {
-    if (likely (count < elements.length))
+    if (likely (count < LIMIT))
       elements[count++] = v;
     else
       set_error ();
   }
   ELEM &push ()
   {
-    if (likely (count < elements.length))
+    if (likely (count < LIMIT))
       return elements[count++];
     else
     {
@@ -424,7 +399,7 @@ struct cff_stack_t
 
   const ELEM& peek ()
   {
-    if (unlikely (count < 0))
+    if (unlikely (count == 0))
     {
       set_error ();
       return Null (ELEM);
@@ -434,7 +409,7 @@ struct cff_stack_t
 
   void unpop ()
   {
-    if (likely (count < elements.length))
+    if (likely (count < LIMIT))
       count++;
     else
       set_error ();
@@ -442,18 +417,19 @@ struct cff_stack_t
 
   void clear () { count = 0; }
 
-  bool in_error () const { return (error || elements.in_error ()); }
+  bool in_error () const { return (error); }
   void set_error ()      { error = true; }
 
   unsigned int get_count () const { return count; }
   bool is_empty () const          { return !count; }
 
-  static constexpr unsigned kSizeLimit = LIMIT;
+  hb_array_t<const ELEM> sub_array (unsigned start, unsigned length) const
+  { return hb_array_t<const ELEM> (elements).sub_array (start, length); }
 
-  protected:
-  bool error;
-  unsigned int count;
-  hb_vector_t<ELEM> elements;
+  private:
+  bool error = false;
+  unsigned int count = 0;
+  ELEM elements[LIMIT];
 };
 
 /* argument stack */
@@ -508,9 +484,6 @@ struct arg_stack_t : cff_stack_t<ARG, 513>
     return true;
   }
 
-  hb_array_t<const ARG> get_subarray (unsigned int start) const
-  { return S::elements.sub_array (start); }
-
   private:
   typedef cff_stack_t<ARG, 513> S;
 };
@@ -518,8 +491,8 @@ struct arg_stack_t : cff_stack_t<ARG, 513>
 /* an operator prefixed by its operands in a byte string */
 struct op_str_t
 {
+  hb_ubytes_t str;
   op_code_t  op;
-  byte_str_t str;
 };
 
 /* base of OP_SERIALIZER */
@@ -547,11 +520,16 @@ struct parsed_values_t
   }
   void fini () { values.fini (); }
 
+  void alloc (unsigned n)
+  {
+    values.alloc (n);
+  }
+
   void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
   {
     VAL *val = values.push ();
     val->op = op;
-    val->str = str_ref.str.sub_str (opStart, str_ref.offset - opStart);
+    val->str = str_ref.str.sub_array (opStart, str_ref.offset - opStart);
     opStart = str_ref.offset;
   }
 
@@ -559,14 +537,14 @@ struct parsed_values_t
   {
     VAL *val = values.push (v);
     val->op = op;
-    val->str = str_ref.sub_str ( opStart, str_ref.offset - opStart);
+    val->str = str_ref.sub_array ( opStart, str_ref.offset - opStart);
     opStart = str_ref.offset;
   }
 
   bool has_op (op_code_t op) const
   {
-    for (unsigned int i = 0; i < get_count (); i++)
-      if (get_value (i).op == op) return true;
+    for (const auto& v : values)
+      if (v.op == op) return true;
     return false;
   }
 
@@ -581,14 +559,11 @@ struct parsed_values_t
 template <typename ARG=number_t>
 struct interp_env_t
 {
-  void init (const byte_str_t &str_)
+  interp_env_t () {}
+  interp_env_t (const hb_ubytes_t &str_)
   {
     str_ref.reset (str_);
-    argStack.init ();
-    error = false;
   }
-  void fini () { argStack.fini (); }
-
   bool in_error () const
   { return error || str_ref.in_error () || argStack.in_error (); }
 
@@ -622,10 +597,10 @@ struct interp_env_t
   arg_stack_t<ARG>
 		argStack;
   protected:
-  bool		error;
+  bool		error = false;
 };
 
-typedef interp_env_t<> num_interp_env_t;
+using num_interp_env_t =  interp_env_t<>;
 
 template <typename ARG=number_t>
 struct opset_t
@@ -668,11 +643,8 @@ struct opset_t
 template <typename ENV>
 struct interpreter_t
 {
-  ~interpreter_t() { fini (); }
-
-  void fini () { env.fini (); }
-
-  ENV env;
+  interpreter_t (ENV& env_) : env (env_) {}
+  ENV& env;
 };
 
 } /* namespace CFF */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh
index ef299369b5e63efeecf7124e6ba92fc0b1842cc7..2983ae54a1ed3efc65c47a276cbb570dc406f846 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh
@@ -79,10 +79,10 @@ struct biased_subrs_t
   unsigned int get_count () const { return subrs ? subrs->count : 0; }
   unsigned int get_bias () const  { return bias; }
 
-  byte_str_t operator [] (unsigned int index) const
+  hb_ubytes_t operator [] (unsigned int index) const
   {
     if (unlikely (!subrs || index >= subrs->count))
-      return Null (byte_str_t);
+      return hb_ubytes_t ();
     else
       return (*subrs)[index];
   }
@@ -112,10 +112,9 @@ struct point_t
 template <typename ARG, typename SUBRS>
 struct cs_interp_env_t : interp_env_t<ARG>
 {
-  void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_)
+  cs_interp_env_t (const hb_ubytes_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) :
+    interp_env_t<ARG> (str)
   {
-    interp_env_t<ARG>::init (str);
-
     context.init (str, CSType_CharString);
     seen_moveto = true;
     seen_hintmask = false;
@@ -123,15 +122,11 @@ struct cs_interp_env_t : interp_env_t<ARG>
     vstem_count = 0;
     hintmask_size = 0;
     pt.set_int (0, 0);
-    callStack.init ();
     globalSubrs.init (globalSubrs_);
     localSubrs.init (localSubrs_);
   }
-  void fini ()
+  ~cs_interp_env_t ()
   {
-    interp_env_t<ARG>::fini ();
-
-    callStack.fini ();
     globalSubrs.fini ();
     localSubrs.fini ();
   }
@@ -880,6 +875,8 @@ struct path_procs_t
 template <typename ENV, typename OPSET, typename PARAM>
 struct cs_interpreter_t : interpreter_t<ENV>
 {
+  cs_interpreter_t (ENV& env_) : interpreter_t<ENV> (env_) {}
+
   bool interpret (PARAM& param)
   {
     SUPER::env.set_endchar (false);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh
index a520ca3bce50b6f0ae6a28221a3e5b78595c3f63..79fe9b42c5f79393809d59ca21d6eda48385e11d 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh
@@ -179,6 +179,8 @@ struct top_dict_opset_t : dict_opset_t
 template <typename OPSET, typename PARAM, typename ENV=num_interp_env_t>
 struct dict_interpreter_t : interpreter_t<ENV>
 {
+  dict_interpreter_t (ENV& env_) : interpreter_t<ENV> (env_) {}
+
   bool interpret (PARAM& param)
   {
     param.init ();
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh
index 1c8762c172b2442737e75d5df18c7457bd3ac201..b306c2ecc9a9190c234340e3f904eb56071b3d6f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh
@@ -38,17 +38,15 @@ typedef biased_subrs_t<CFF1Subrs>   cff1_biased_subrs_t;
 struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs>
 {
   template <typename ACC>
-  void init (const byte_str_t &str, ACC &acc, unsigned int fd)
+  cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd)
+    : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs)
   {
-    SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
     processed_width = false;
     has_width = false;
     arg_start = 0;
     in_seac = false;
   }
 
-  void fini () { SUPER::fini (); }
-
   void set_width (bool has_width_)
   {
     if (likely (!processed_width && (SUPER::argStack.get_count () > 0)))
@@ -154,7 +152,7 @@ struct cff1_cs_opset_t : cs_opset_t<number_t, OPSET, cff1_cs_interp_env_t, PARAM
 };
 
 template <typename OPSET, typename PARAM>
-struct cff1_cs_interpreter_t : cs_interpreter_t<cff1_cs_interp_env_t, OPSET, PARAM> {};
+using cff1_cs_interpreter_t = cs_interpreter_t<cff1_cs_interp_env_t, OPSET, PARAM>;
 
 } /* namespace CFF */
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh
index 766183760e099a5a8df03abe2ad3cd41f51e7067..d0b9e7b086366ec3062d38f7e671ee74594c495e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh
@@ -64,14 +64,14 @@ struct blend_arg_t : number_t
 typedef interp_env_t<blend_arg_t> BlendInterpEnv;
 typedef biased_subrs_t<CFF2Subrs>   cff2_biased_subrs_t;
 
-struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
+template <typename ELEM>
+struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
 {
   template <typename ACC>
-  void init (const byte_str_t &str, ACC &acc, unsigned int fd,
-	     const int *coords_=nullptr, unsigned int num_coords_=0)
+  cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd,
+			const int *coords_=nullptr, unsigned int num_coords_=0)
+    : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs)
   {
-    SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
-
     coords = coords_;
     num_coords = num_coords_;
     varStore = acc.varStore;
@@ -100,18 +100,14 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
       return OpCode_return;
   }
 
-  const blend_arg_t& eval_arg (unsigned int i)
+  const ELEM& eval_arg (unsigned int i)
   {
-    blend_arg_t  &arg = argStack[i];
-    blend_arg (arg);
-    return arg;
+    return SUPER::argStack[i];
   }
 
-  const blend_arg_t& pop_arg ()
+  const ELEM& pop_arg ()
   {
-    blend_arg_t  &arg = argStack.pop ();
-    blend_arg (arg);
-    return arg;
+    return SUPER::argStack.pop ();
   }
 
   void process_blend ()
@@ -122,7 +118,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
       if (do_blend)
       {
 	if (unlikely (!scalars.resize (region_count)))
-	  set_error ();
+	  SUPER::set_error ();
 	else
 	  varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords,
 						 &scalars[0], region_count);
@@ -133,10 +129,10 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
 
   void process_vsindex ()
   {
-    unsigned int  index = argStack.pop_uint ();
+    unsigned int  index = SUPER::argStack.pop_uint ();
     if (unlikely (seen_vsindex () || seen_blend))
     {
-      set_error ();
+     SUPER::set_error ();
     }
     else
     {
@@ -151,22 +147,18 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
   void	 set_ivs (unsigned int ivs_) { ivs = ivs_; }
   bool	 seen_vsindex () const { return seen_vsindex_; }
 
-  protected:
-  void blend_arg (blend_arg_t &arg)
+  double blend_deltas (hb_array_t<const ELEM> deltas) const
   {
-    if (do_blend && arg.blending ())
+    double v = 0;
+    if (do_blend)
     {
-      if (likely (scalars.length == arg.deltas.length))
+      if (likely (scalars.length == deltas.length))
       {
-	double v = arg.to_real ();
 	for (unsigned int i = 0; i < scalars.length; i++)
-	{
-	  v += (double)scalars[i] * arg.deltas[i].to_real ();
-	}
-	arg.set_real (v);
-	arg.deltas.resize (0);
+	  v += (double) scalars[i] * deltas[i].to_real ();
       }
     }
+    return v;
   }
 
   protected:
@@ -180,22 +172,24 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
   bool	  seen_vsindex_;
   bool	  seen_blend;
 
-  typedef cs_interp_env_t<blend_arg_t, CFF2Subrs> SUPER;
+  typedef cs_interp_env_t<ELEM, CFF2Subrs> SUPER;
 };
-template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t, PARAM>>
-struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>
+template <typename OPSET, typename PARAM, typename ELEM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t<ELEM>, PARAM>>
+struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH>
 {
-  static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param)
+  static void process_op (op_code_t op, cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
   {
     switch (op) {
       case OpCode_callsubr:
       case OpCode_callgsubr:
 	/* a subroutine number shouldn't be a blended value */
+#if 0
 	if (unlikely (env.argStack.peek ().blending ()))
 	{
 	  env.set_error ();
 	  break;
 	}
+#endif
 	SUPER::process_op (op, env, param);
 	break;
 
@@ -204,11 +198,13 @@ struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PA
 	break;
 
       case OpCode_vsindexcs:
+#if 0
 	if (unlikely (env.argStack.peek ().blending ()))
 	{
 	  env.set_error ();
 	  break;
 	}
+#endif
 	OPSET::process_vsindex (env, param);
 	break;
 
@@ -217,7 +213,26 @@ struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PA
     }
   }
 
-  static void process_blend (cff2_cs_interp_env_t &env, PARAM& param)
+  template <typename T = ELEM,
+	    hb_enable_if (hb_is_same (T, blend_arg_t))>
+  static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env,
+				 ELEM &arg,
+				 const hb_array_t<const ELEM> blends,
+				 unsigned n, unsigned i)
+  {
+    arg.set_blends (n, i, blends.length, blends);
+  }
+  template <typename T = ELEM,
+	    hb_enable_if (!hb_is_same (T, blend_arg_t))>
+  static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env,
+				 ELEM &arg,
+				 const hb_array_t<const ELEM> blends,
+				 unsigned n, unsigned i)
+  {
+    arg.set_real (arg.to_real () + env.blend_deltas (blends));
+  }
+
+  static void process_blend (cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
   {
     unsigned int n, k;
 
@@ -234,26 +249,26 @@ struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PA
     }
     for (unsigned int i = 0; i < n; i++)
     {
-      const hb_array_t<const blend_arg_t>	blends = env.argStack.get_subarray (start + n + (i * k));
-      env.argStack[start + i].set_blends (n, i, k, blends);
+      const hb_array_t<const ELEM> blends = env.argStack.sub_array (start + n + (i * k), k);
+      process_arg_blend (env, env.argStack[start + i], blends, n, i);
     }
 
     /* pop off blend values leaving default values now adorned with blend values */
     env.argStack.pop (k * n);
   }
 
-  static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param)
+  static void process_vsindex (cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
   {
     env.process_vsindex ();
     env.clear_args ();
   }
 
   private:
-  typedef cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>  SUPER;
+  typedef cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH>  SUPER;
 };
 
-template <typename OPSET, typename PARAM>
-struct cff2_cs_interpreter_t : cs_interpreter_t<cff2_cs_interp_env_t, OPSET, PARAM> {};
+template <typename OPSET, typename PARAM, typename ELEM>
+using cff2_cs_interpreter_t = cs_interpreter_t<cff2_cs_interp_env_t<ELEM>, OPSET, PARAM>;
 
 } /* namespace CFF */
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-common.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-common.cc
index 41229b9183ef996493113a62aa8b891b1cd7b857..7266d9b01f4430d621e89f08d829d0802737e8ea 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-common.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-common.cc
@@ -160,7 +160,7 @@ hb_tag_to_string (hb_tag_t tag, char *buf)
 
 /* hb_direction_t */
 
-const char direction_strings[][4] = {
+static const char direction_strings[][4] = {
   "ltr",
   "rtl",
   "ttb",
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh
index 4b46dea9384388b9545f0d7eed0385467ecc7050..2578231d2388997b2042377e12a6a1e7bd69054a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh
@@ -64,6 +64,7 @@
 #define HB_NO_FACE_COLLECT_UNICODES
 #define HB_NO_GETENV
 #define HB_NO_HINTING
+#define HB_NO_LANGUAGE_LONG
 #define HB_NO_LANGUAGE_PRIVATE_SUBTAG
 #define HB_NO_LAYOUT_FEATURE_PARAMS
 #define HB_NO_LAYOUT_COLLECT_GLYPHS
@@ -145,10 +146,10 @@
 #endif
 
 #ifdef HB_NO_OT_SHAPE_FALLBACK
-#define HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK
-#define HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK
-#define HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK
-#define HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS
+#define HB_NO_OT_SHAPER_ARABIC_FALLBACK
+#define HB_NO_OT_SHAPER_HEBREW_FALLBACK
+#define HB_NO_OT_SHAPER_THAI_FALLBACK
+#define HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS
 #endif
 
 #ifdef NDEBUG
@@ -163,5 +164,9 @@
 #endif
 #endif
 
+#ifdef HB_OPTIMIZE_SIZE
+#define HB_NO_OT_LAYOUT_LOOKUP_CACHE
+#endif
+
 
 #endif /* HB_CONFIG_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext.cc
index 6ccc1b0a2b96448d5eb00d65d9df84e4bb85f910..99b33c001e5a4acafe68e86d64d7397a69952d30 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext.cc
@@ -332,7 +332,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
     return nullptr;
   }
 
-  if (font->coords)
+  if (font->num_coords)
   {
     CFMutableDictionaryRef variations =
       CFDictionaryCreateMutable (kCFAllocatorDefault,
@@ -379,37 +379,6 @@ _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
   CFRelease ((CTFontRef) data);
 }
 
-static const hb_coretext_font_data_t *
-hb_coretext_font_data_sync (hb_font_t *font)
-{
-retry:
-  const hb_coretext_font_data_t *data = font->data.coretext;
-  if (unlikely (!data)) return nullptr;
-
-  if (fabs (CTFontGetSize ((CTFontRef) data) - (CGFloat) font->ptem) > (CGFloat) .5)
-  {
-    /* XXX-MT-bug
-     * Note that evaluating condition above can be dangerous if another thread
-     * got here first and destructed data.  That's, as always, bad use pattern.
-     * If you modify the font (change font size), other threads must not be
-     * using it at the same time.  However, since this check is delayed to
-     * when one actually tries to shape something, this is a XXX race condition
-     * (and the only one we have that I know of) right now.  Ie. you modify the
-     * font size in one thread, then (supposedly safely) try to use it from two
-     * or more threads and BOOM!  I'm not sure how to fix this.  We want RCU.
-     */
-
-    /* Drop and recreate. */
-    /* If someone dropped it in the mean time, throw it away and don't touch it.
-     * Otherwise, destruct it. */
-    if (likely (font->data.coretext.cmpexch (const_cast<hb_coretext_font_data_t *> (data), nullptr)))
-      _hb_coretext_shaper_font_data_destroy (const_cast<hb_coretext_font_data_t *> (data));
-    else
-      goto retry;
-  }
-  return font->data.coretext;
-}
-
 /**
  * hb_coretext_font_create:
  * @ct_font: The CTFontRef to work upon
@@ -455,8 +424,8 @@ hb_coretext_font_create (CTFontRef ct_font)
 CTFontRef
 hb_coretext_font_get_ct_font (hb_font_t *font)
 {
-  const hb_coretext_font_data_t *data = hb_coretext_font_data_sync (font);
-  return data ? (CTFontRef) data : nullptr;
+  CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext;
+  return ct_font ? (CTFontRef) ct_font : nullptr;
 }
 
 
@@ -516,7 +485,7 @@ _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 {
   hb_face_t *face = font->face;
   CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
-  CTFontRef ct_font = (CTFontRef) hb_coretext_font_data_sync (font);
+  CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext;
 
   CGFloat ct_font_size = CTFontGetSize (ct_font);
   CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size;
@@ -1106,7 +1075,8 @@ resize_and_retry:
 	      advance = positions[j + 1].x - positions[j].x;
 	    else /* last glyph */
 	      advance = run_advance - (positions[j].x - positions[0].x);
-	    info->mask = round (advance * x_mult);
+	    /* int cast necessary to pass through negative values. */
+	    info->mask = (int) round (advance * x_mult);
 	    info->var1.i32 = x_offset;
 	    info->var2.i32 = round (positions[j].y * y_mult);
 	    info++;
@@ -1122,7 +1092,8 @@ resize_and_retry:
 	      advance = positions[j + 1].y - positions[j].y;
 	    else /* last glyph */
 	      advance = run_advance - (positions[j].y - positions[0].y);
-	    info->mask = round (advance * y_mult);
+	    /* int cast necessary to pass through negative values. */
+	    info->mask = (int) round (advance * y_mult);
 	    info->var1.i32 = round (positions[j].x * x_mult);
 	    info->var2.i32 = y_offset;
 	    info++;
@@ -1151,7 +1122,7 @@ resize_and_retry:
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
 
-	info++, pos++;
+	info++; pos++;
       }
     else
       for (unsigned int i = 0; i < count; i++)
@@ -1160,7 +1131,7 @@ resize_and_retry:
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
 
-	info++, pos++;
+	info++; pos++;
       }
 
     /* Fix up clusters so that we never return out-of-order indices;
@@ -1173,7 +1144,8 @@ resize_and_retry:
      * This does *not* mean we'll form the same clusters as Uniscribe
      * or the native OT backend, only that the cluster indices will be
      * monotonic in the output buffer. */
-    if (count > 1 && (status_or & kCTRunStatusNonMonotonic))
+    if (count > 1 && (status_or & kCTRunStatusNonMonotonic) &&
+	buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
     {
       hb_glyph_info_t *info = buffer->info;
       if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
@@ -1197,6 +1169,10 @@ resize_and_retry:
     }
   }
 
+  /* TODO: Sometimes the above positioning code generates negative
+   * advance values. Fix them up. Example, with NotoNastaliqUrdu
+   * font and sequence ابهد. */
+
   buffer->clear_glyph_flags ();
   buffer->unsafe_to_break ();
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-cplusplus.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-cplusplus.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c79d1550649a07fd45e46f27b4312d072e302625
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-cplusplus.hh
@@ -0,0 +1,195 @@
+/*
+ * Copyright © 2022 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.
+ */
+
+#ifndef HB_CPLUSPLUS_HH
+#define HB_CPLUSPLUS_HH
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+HB_END_DECLS
+
+#ifdef __cplusplus
+
+#include <functional>
+#include <utility>
+
+#if 0
+#if !(__cplusplus >= 201103L)
+#error "HarfBuzz C++ helpers require C++11"
+#endif
+#endif
+
+namespace hb {
+
+
+template <typename T>
+struct vtable;
+
+template <typename T>
+struct shared_ptr
+{
+  using element_type = T;
+
+  using v = vtable<T>;
+
+  explicit shared_ptr (T *p = nullptr) : p (p) {}
+  shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {}
+  shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; }
+  shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; }
+  shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
+  ~shared_ptr () { v::destroy (p); p = nullptr; }
+
+  T* get() const { return p; }
+
+  void swap (shared_ptr &o) { std::swap (p, o.p); }
+  friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); }
+
+  operator T * () const { return p; }
+  T& operator * () const { return *get (); }
+  T* operator -> () const { return get (); }
+  operator bool () { return p; }
+  bool operator == (const shared_ptr &o) { return p == o.p; }
+  bool operator != (const shared_ptr &o) { return p != o.p; }
+
+  static T* get_empty() { return v::get_empty (); }
+  T* reference() { return v::reference (p); }
+  void destroy() { v::destroy (p); }
+  void set_user_data (hb_user_data_key_t *key,
+		      void *value,
+		      hb_destroy_func_t destroy,
+		      hb_bool_t replace) { v::set_user_data (p, key, value, destroy, replace); } 
+  void * get_user_data (hb_user_data_key_t *key) { return v::get_user_data (p, key); }
+
+  private:
+  T *p;
+};
+
+template<typename T> struct is_shared_ptr : std::false_type {};
+template<typename T> struct is_shared_ptr<shared_ptr<T>> : std::true_type {};
+
+template <typename T>
+struct unique_ptr
+{
+  using element_type = T;
+
+  using v = vtable<T>;
+
+  explicit unique_ptr (T *p = nullptr) : p (p) {}
+  unique_ptr (const unique_ptr &o) = delete;
+  unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; }
+  unique_ptr& operator = (const unique_ptr &o) = delete;
+  unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
+  ~unique_ptr () { v::destroy (p); p = nullptr; }
+
+  T* get() const { return p; }
+  T* release () { T* v = p; p = nullptr; return v; }
+
+  void swap (unique_ptr &o) { std::swap (p, o.p); }
+  friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); }
+
+  operator T * () const { return p; }
+  T& operator * () const { return *get (); }
+  T* operator -> () const { return get (); }
+  operator bool () { return p; }
+
+  private:
+  T *p;
+};
+
+template<typename T> struct is_unique_ptr : std::false_type {};
+template<typename T> struct is_unique_ptr<unique_ptr<T>> : std::true_type {};
+
+template <typename T,
+	  T * (*_get_empty) (void),
+	  T * (*_reference) (T *),
+	  void (*_destroy) (T *),
+	  hb_bool_t (*_set_user_data) (T *,
+				       hb_user_data_key_t *,
+				       void *,
+				       hb_destroy_func_t,
+				       hb_bool_t),
+	  void * (*_get_user_data) (T *,
+				    hb_user_data_key_t *)>
+struct vtable_t
+{
+  static constexpr auto get_empty = _get_empty;
+  static constexpr auto reference = _reference;
+  static constexpr auto destroy = _destroy;
+  static constexpr auto set_user_data = _set_user_data;
+  static constexpr auto get_user_data = _get_user_data;
+};
+
+#define HB_DEFINE_VTABLE(name) \
+	template<> \
+	struct vtable<hb_##name##_t> \
+	     : vtable_t<hb_##name##_t, \
+			&hb_##name##_get_empty, \
+			&hb_##name##_reference, \
+			&hb_##name##_destroy, \
+			&hb_##name##_set_user_data, \
+			&hb_##name##_get_user_data> {}
+
+HB_DEFINE_VTABLE (buffer);
+HB_DEFINE_VTABLE (blob);
+HB_DEFINE_VTABLE (face);
+HB_DEFINE_VTABLE (font);
+HB_DEFINE_VTABLE (font_funcs);
+HB_DEFINE_VTABLE (map);
+HB_DEFINE_VTABLE (set);
+HB_DEFINE_VTABLE (shape_plan);
+HB_DEFINE_VTABLE (unicode_funcs);
+
+#undef HB_DEFINE_VTABLE
+
+
+} // namespace hb
+
+namespace std {
+
+template<typename T>
+struct hash<hb::shared_ptr<T>>
+{
+    size_t operator()(const hb::shared_ptr<T>& v) const noexcept
+    {
+        size_t h = hash<decltype (v.get ())>{}(v.get ());
+        return h;
+    }
+};
+
+template<typename T>
+struct hash<hb::unique_ptr<T>>
+{
+    size_t operator()(const hb::unique_ptr<T>& v) const noexcept
+    {
+        size_t h = hash<decltype (v.get ())>{}(v.get ());
+        return h;
+    }
+};
+
+} // namespace std
+
+#endif /* __cplusplus */
+
+#endif /* HB_CPLUSPLUS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc
index f177ff31c09ec7b3ea77c8a3d0bdba22790063bc..ce04f5bab1898f2242a6905ea85f453ba5fab3b8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc
@@ -241,17 +241,12 @@ struct hb_directwrite_font_data_t {};
 hb_directwrite_font_data_t *
 _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 {
-  hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
-  if (unlikely (!data))
-    return nullptr;
-
-  return data;
+  return (hb_directwrite_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
 _hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
 {
-  delete data;
 }
 
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.cc
index b31019b07e0c67733162c9fdd1719f32e057547b..fdc71102d6499f42e8a497b14938d04be19d12d4 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.cc
@@ -56,12 +56,14 @@ hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data,
 			  float to_x, float to_y,
 			  void *user_data HB_UNUSED)
 {
+#define HB_ONE_THIRD 0.33333333f
   dfuncs->emit_cubic_to (draw_data, *st,
-			 (st->current_x + 2.f * control_x) / 3.f,
-			 (st->current_y + 2.f * control_y) / 3.f,
-			 (to_x + 2.f * control_x) / 3.f,
-			 (to_y + 2.f * control_y) / 3.f,
+			 (st->current_x + 2.f * control_x) * HB_ONE_THIRD,
+			 (st->current_y + 2.f * control_y) * HB_ONE_THIRD,
+			 (to_x + 2.f * control_x) * HB_ONE_THIRD,
+			 (to_y + 2.f * control_y) * HB_ONE_THIRD,
 			 to_x, to_y);
+#undef HB_ONE_THIRD
 }
 
 static void
@@ -89,25 +91,46 @@ hb_draw_funcs_set_##name##_func (hb_draw_funcs_t	 *dfuncs,		\
   if (hb_object_is_immutable (dfuncs))						\
     return;									\
 										\
-  if (dfuncs->destroy.name)							\
-    dfuncs->destroy.name (dfuncs->user_data.name);				\
-										\
-  if (func) {									\
-    dfuncs->func.name = func;							\
-    dfuncs->user_data.name = user_data;						\
-    dfuncs->destroy.name = destroy;						\
-  } else {									\
-    dfuncs->func.name = hb_draw_##name##_nil;					\
-    dfuncs->user_data.name = nullptr;						\
-    dfuncs->destroy.name = nullptr;						\
-  }										\
+  if (dfuncs->destroy && dfuncs->destroy->name)					\
+    dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \
+									 \
+  if (user_data && !dfuncs->user_data)                                   \
+  {                                                                      \
+    dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); \
+    if (unlikely (!dfuncs->user_data))                                   \
+      goto fail;                                                         \
+  }                                                                      \
+  if (destroy && !dfuncs->destroy)                                       \
+  {                                                                      \
+    dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); \
+    if (unlikely (!dfuncs->destroy))                                     \
+      goto fail;                                                         \
+  }                                                                      \
+									\
+  if (func) {								\
+    dfuncs->func.name = func;						\
+    if (dfuncs->user_data)						\
+      dfuncs->user_data->name = user_data;				\
+    if (dfuncs->destroy)						\
+      dfuncs->destroy->name = destroy;					\
+  } else {								\
+    dfuncs->func.name = hb_draw_##name##_nil;				\
+    if (dfuncs->user_data)						\
+      dfuncs->user_data->name = nullptr;				\
+    if (dfuncs->destroy)						\
+      dfuncs->destroy->name = nullptr;					\
+  }									\
+                                                                         \
+fail:                                                                    \
+  if (destroy)                                                           \
+    destroy (user_data);                                                 \
 }
 
 HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_DRAW_FUNC_IMPLEMENT
 
 /**
- * hb_draw_funcs_create: (Xconstructor)
+ * hb_draw_funcs_create:
  *
  * Creates a new draw callbacks object.
  *
@@ -177,11 +200,13 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs)
 {
   if (!hb_object_destroy (dfuncs)) return;
 
+  if (dfuncs->destroy)
+  {
 #define HB_DRAW_FUNC_IMPLEMENT(name) \
-  if (dfuncs->destroy.name) dfuncs->destroy.name (dfuncs->user_data.name);
-    HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
+    if (dfuncs->destroy->name) dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name);
+      HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_DRAW_FUNC_IMPLEMENT
-
+  }
 
   hb_free (dfuncs);
 }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.hh
index 28bc9218e182c8e31ced62eccf65ed1afa6d2ad6..768f51a87566831ea729651df02c5dc25542bab0 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-draw.hh
@@ -54,31 +54,31 @@ struct hb_draw_funcs_t
 #define HB_DRAW_FUNC_IMPLEMENT(name) void *name;
     HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_DRAW_FUNC_IMPLEMENT
-  } user_data;
+  } *user_data;
 
   struct {
 #define HB_DRAW_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
     HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_DRAW_FUNC_IMPLEMENT
-  } destroy;
+  } *destroy;
 
   void emit_move_to (void *draw_data, hb_draw_state_t &st,
 		     float to_x, float to_y)
   { func.move_to (this, draw_data, &st,
 		  to_x, to_y,
-		  user_data.move_to); }
+		  !user_data ? nullptr : user_data->move_to); }
   void emit_line_to (void *draw_data, hb_draw_state_t &st,
 		     float to_x, float to_y)
   { func.line_to (this, draw_data, &st,
 		  to_x, to_y,
-		  user_data.line_to); }
+		  !user_data ? nullptr : user_data->line_to); }
   void emit_quadratic_to (void *draw_data, hb_draw_state_t &st,
 			  float control_x, float control_y,
 			  float to_x, float to_y)
   { func.quadratic_to (this, draw_data, &st,
 		       control_x, control_y,
 		       to_x, to_y,
-		       user_data.quadratic_to); }
+		       !user_data ? nullptr : user_data->quadratic_to); }
   void emit_cubic_to (void *draw_data, hb_draw_state_t &st,
 		      float control1_x, float control1_y,
 		      float control2_x, float control2_y,
@@ -87,10 +87,10 @@ struct hb_draw_funcs_t
 		   control1_x, control1_y,
 		   control2_x, control2_y,
 		   to_x, to_y,
-		   user_data.cubic_to); }
+		   !user_data ? nullptr : user_data->cubic_to); }
   void emit_close_path (void *draw_data, hb_draw_state_t &st)
   { func.close_path (this, draw_data, &st,
-		     user_data.close_path); }
+		     !user_data ? nullptr : user_data->close_path); }
 
 
   void move_to (void *draw_data, hb_draw_state_t &st,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc
index 5365598636fcfbe8615949d5cba85679f21aa594..7cdd6cb49fb84c15443b00f0047cb291a8115dbd 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc
@@ -190,7 +190,7 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
 }
 
 /**
- * hb_face_create: (Xconstructor)
+ * hb_face_create:
  * @blob: #hb_blob_t to work upon
  * @index: The index of the face within @blob
  *
@@ -342,7 +342,7 @@ hb_face_set_user_data (hb_face_t          *face,
  * Since: 0.9.2
  **/
 void *
-hb_face_get_user_data (const hb_face_t    *face,
+hb_face_get_user_data (hb_face_t          *face,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (face, key);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h
index 6ef2f8b8861f15eb78ee70635b02cea3aeba4f48..3b7477a76c0316ef0066dfa1432fd5f1bea26065 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h
@@ -96,7 +96,7 @@ hb_face_set_user_data (hb_face_t          *face,
 		       hb_bool_t           replace);
 
 HB_EXTERN void *
-hb_face_get_user_data (const hb_face_t    *face,
+hb_face_get_user_data (hb_face_t          *face,
 		       hb_user_data_key_t *key);
 
 HB_EXTERN void
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc
index db05f017a5e420683710e6ddb7192b4cb27975c1..8c4fe863b65d2be608008d46e00a70a61c28d683 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc
@@ -636,16 +636,8 @@ DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
 {
   HB_OBJECT_HEADER_STATIC,
 
-  {
-#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
-    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_FONT_FUNC_IMPLEMENT
-  },
-  {
-#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
-    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_FONT_FUNC_IMPLEMENT
-  },
+  nullptr,
+  nullptr,
   {
     {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
@@ -658,16 +650,8 @@ DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
 static const hb_font_funcs_t _hb_font_funcs_default = {
   HB_OBJECT_HEADER_STATIC,
 
-  {
-#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
-    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_FONT_FUNC_IMPLEMENT
-  },
-  {
-#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
-    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_FONT_FUNC_IMPLEMENT
-  },
+  nullptr,
+  nullptr,
   {
     {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default,
@@ -679,7 +663,7 @@ static const hb_font_funcs_t _hb_font_funcs_default = {
 
 
 /**
- * hb_font_funcs_create: (Xconstructor)
+ * hb_font_funcs_create:
  *
  * Creates a new #hb_font_funcs_t structure of font functions.
  *
@@ -746,10 +730,16 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
 {
   if (!hb_object_destroy (ffuncs)) return;
 
-#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \
-  ffuncs->destroy.name (ffuncs->user_data.name);
-  HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+  if (ffuncs->destroy)
+  {
+#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy->name) \
+    ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name);
+    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
+  }
+
+  hb_free (ffuncs->destroy);
+  hb_free (ffuncs->user_data);
 
   hb_free (ffuncs);
 }
@@ -841,24 +831,50 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t             *ffuncs,    \
 				 hb_destroy_func_t            destroy)   \
 {                                                                        \
   if (hb_object_is_immutable (ffuncs))                                   \
+    goto fail;                                                           \
+                                                                         \
+  if (!func)                                                             \
   {                                                                      \
     if (destroy)                                                         \
       destroy (user_data);                                               \
-    return;                                                              \
+    destroy = nullptr;                                                   \
+    user_data = nullptr;                                                 \
   }                                                                      \
 									 \
-  if (ffuncs->destroy.name)                                              \
-    ffuncs->destroy.name (ffuncs->user_data.name);                       \
+  if (ffuncs->destroy && ffuncs->destroy->name)                          \
+    ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); \
+                                                                         \
+  if (user_data && !ffuncs->user_data)                                   \
+  {                                                                      \
+    ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); \
+    if (unlikely (!ffuncs->user_data))                                   \
+      goto fail;                                                         \
+  }                                                                      \
+  if (destroy && !ffuncs->destroy)                                       \
+  {                                                                      \
+    ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); \
+    if (unlikely (!ffuncs->destroy))                                     \
+      goto fail;                                                         \
+  }                                                                      \
 									 \
   if (func) {                                                            \
     ffuncs->get.f.name = func;                                           \
-    ffuncs->user_data.name = user_data;                                  \
-    ffuncs->destroy.name = destroy;                                      \
+    if (ffuncs->user_data)                                               \
+      ffuncs->user_data->name = user_data;                               \
+    if (ffuncs->destroy)                                                 \
+      ffuncs->destroy->name = destroy;                                   \
   } else {                                                               \
     ffuncs->get.f.name = hb_font_get_##name##_default;                   \
-    ffuncs->user_data.name = nullptr;                                    \
-    ffuncs->destroy.name = nullptr;                                      \
+    if (ffuncs->user_data)                                               \
+      ffuncs->user_data->name = nullptr;                                 \
+    if (ffuncs->destroy)						 \
+      ffuncs->destroy->name = nullptr;                                   \
   }                                                                      \
+  return;                                                                \
+                                                                         \
+fail:                                                                    \
+  if (destroy)                                                           \
+    destroy (user_data);                                                 \
 }
 
 HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
@@ -1623,6 +1639,9 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
 {
   HB_OBJECT_HEADER_STATIC,
 
+  0, /* serial */
+  0, /* serial_coords */
+
   nullptr, /* parent */
   const_cast<hb_face_t *> (&_hb_Null_hb_face_t),
 
@@ -1630,6 +1649,8 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
   1000, /* y_scale */
   0., /* slant */
   0., /* slant_xy; */
+  1.f, /* x_multf */
+  1.f, /* y_multf */
   1<<16, /* x_mult */
   1<<16, /* y_mult */
 
@@ -1662,14 +1683,15 @@ _hb_font_create (hb_face_t *face)
   font->face = hb_face_reference (face);
   font->klass = hb_font_funcs_get_empty ();
   font->data.init0 (font);
-  font->x_scale = font->y_scale = hb_face_get_upem (face);
+  font->x_scale = font->y_scale = face->get_upem ();
+  font->x_multf = font->y_multf = 1.f;
   font->x_mult = font->y_mult = 1 << 16;
 
   return font;
 }
 
 /**
- * hb_font_create: (Xconstructor)
+ * hb_font_create:
  * @face: a face.
  *
  * Constructs a new font object from the specified face.
@@ -1715,6 +1737,8 @@ _hb_font_adopt_var_coords (hb_font_t *font,
   font->coords = coords;
   font->design_coords = design_coords;
   font->num_coords = coords_length;
+
+  font->mults_changed (); // Easiest to call this to drop cached data
 }
 
 /**
@@ -1744,7 +1768,6 @@ hb_font_create_sub_font (hb_font_t *parent)
   font->x_scale = parent->x_scale;
   font->y_scale = parent->y_scale;
   font->slant = parent->slant;
-  font->mults_changed ();
   font->x_ppem = parent->x_ppem;
   font->y_ppem = parent->y_ppem;
   font->ptem = parent->ptem;
@@ -1767,6 +1790,8 @@ hb_font_create_sub_font (hb_font_t *parent)
     }
   }
 
+  font->mults_changed ();
+
   return font;
 }
 
@@ -1852,6 +1877,9 @@ hb_font_set_user_data (hb_font_t          *font,
 		       hb_destroy_func_t   destroy /* May be NULL. */,
 		       hb_bool_t           replace)
 {
+  if (!hb_object_is_immutable (font))
+    font->serial++;
+
   return hb_object_set_user_data (font, key, data, destroy, replace);
 }
 
@@ -1910,6 +1938,45 @@ hb_font_is_immutable (hb_font_t *font)
   return hb_object_is_immutable (font);
 }
 
+/**
+ * hb_font_get_serial:
+ * @font: #hb_font_t to work upon
+ *
+ * Returns the internal serial number of the font. The serial
+ * number is increased every time a setting on the font is
+ * changed, using a setter function.
+ *
+ * Return value: serial number
+ *
+ * Since: 4.4.0.
+ **/
+unsigned int
+hb_font_get_serial (hb_font_t *font)
+{
+  return font->serial;
+}
+
+/**
+ * hb_font_changed:
+ * @font: #hb_font_t to work upon
+ *
+ * Notifies the @font that underlying font data has changed.
+ * This has the effect of increasing the serial as returned
+ * by hb_font_get_serial(), which invalidates internal caches.
+ *
+ * Since: 4.4.0.
+ **/
+void
+hb_font_changed (hb_font_t *font)
+{
+  if (hb_object_is_immutable (font))
+    return;
+
+  font->serial++;
+
+  font->mults_changed ();
+}
+
 /**
  * hb_font_set_parent:
  * @font: #hb_font_t to work upon
@@ -1926,6 +1993,11 @@ hb_font_set_parent (hb_font_t *font,
   if (hb_object_is_immutable (font))
     return;
 
+  if (parent == font->parent)
+    return;
+
+  font->serial++;
+
   if (!parent)
     parent = hb_font_get_empty ();
 
@@ -1968,6 +2040,11 @@ hb_font_set_face (hb_font_t *font,
   if (hb_object_is_immutable (font))
     return;
 
+  if (face == font->face)
+    return;
+
+  font->serial++;
+
   if (unlikely (!face))
     face = hb_face_get_empty ();
 
@@ -2022,6 +2099,8 @@ hb_font_set_funcs (hb_font_t         *font,
     return;
   }
 
+  font->serial++;
+
   if (font->destroy)
     font->destroy (font->user_data);
 
@@ -2059,6 +2138,8 @@ hb_font_set_funcs_data (hb_font_t         *font,
     return;
   }
 
+  font->serial++;
+
   if (font->destroy)
     font->destroy (font->user_data);
 
@@ -2085,6 +2166,11 @@ hb_font_set_scale (hb_font_t *font,
   if (hb_object_is_immutable (font))
     return;
 
+  if (font->x_scale == x_scale && font->y_scale == y_scale)
+    return;
+
+  font->serial++;
+
   font->x_scale = x_scale;
   font->y_scale = y_scale;
   font->mults_changed ();
@@ -2127,6 +2213,11 @@ hb_font_set_ppem (hb_font_t    *font,
   if (hb_object_is_immutable (font))
     return;
 
+  if (font->x_ppem == x_ppem && font->y_ppem == y_ppem)
+    return;
+
+  font->serial++;
+
   font->x_ppem = x_ppem;
   font->y_ppem = y_ppem;
 }
@@ -2169,6 +2260,11 @@ hb_font_set_ptem (hb_font_t *font,
   if (hb_object_is_immutable (font))
     return;
 
+  if (font->ptem == ptem)
+    return;
+
+  font->serial++;
+
   font->ptem = ptem;
 }
 
@@ -2216,6 +2312,11 @@ hb_font_set_synthetic_slant (hb_font_t *font, float slant)
   if (hb_object_is_immutable (font))
     return;
 
+  if (font->slant == slant)
+    return;
+
+  font->serial++;
+
   font->slant = slant;
   font->mults_changed ();
 }
@@ -2263,6 +2364,8 @@ hb_font_set_variations (hb_font_t            *font,
   if (hb_object_is_immutable (font))
     return;
 
+  font->serial_coords = ++font->serial;
+
   if (!variations_length)
   {
     hb_font_set_var_coords_normalized (font, nullptr, 0);
@@ -2322,6 +2425,8 @@ hb_font_set_var_coords_design (hb_font_t    *font,
   if (hb_object_is_immutable (font))
     return;
 
+  font->serial_coords = ++font->serial;
+
   int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr;
   float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr;
 
@@ -2355,6 +2460,8 @@ hb_font_set_var_named_instance (hb_font_t *font,
   if (hb_object_is_immutable (font))
     return;
 
+  font->serial_coords = ++font->serial;
+
   unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr);
 
   float *coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr;
@@ -2391,6 +2498,8 @@ hb_font_set_var_coords_normalized (hb_font_t    *font,
   if (hb_object_is_immutable (font))
     return;
 
+  font->serial_coords = ++font->serial;
+
   int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr;
   int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr;
   float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr;
@@ -2596,12 +2705,14 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t          *ffuncs,
     return;
   }
 
+  /* Since we pass it to two destroying functions. */
+  trampoline_reference (&trampoline->closure);
+
   hb_font_funcs_set_nominal_glyph_func (ffuncs,
 					hb_font_get_nominal_glyph_trampoline,
 					trampoline,
 					trampoline_destroy);
 
-  trampoline_reference (&trampoline->closure);
   hb_font_funcs_set_variation_glyph_func (ffuncs,
 					  hb_font_get_variation_glyph_trampoline,
 					  trampoline,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-font.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-font.h
index 95488575354453e35553b01bc430634c3fd02b30..ca6ecf79634ae5d9b723c49196932b1b432364ed 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-font.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-font.h
@@ -1002,6 +1002,12 @@ hb_font_make_immutable (hb_font_t *font);
 HB_EXTERN hb_bool_t
 hb_font_is_immutable (hb_font_t *font);
 
+HB_EXTERN unsigned int
+hb_font_get_serial (hb_font_t *font);
+
+HB_EXTERN void
+hb_font_changed (hb_font_t *font);
+
 HB_EXTERN void
 hb_font_set_parent (hb_font_t *font,
 		    hb_font_t *parent);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-font.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-font.hh
index 70311b4a85d665bcd81d58fa02152827be0095a3..bb402e23eb41ba98f8ac9327a78983d1e3b36d4f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-font.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-font.hh
@@ -68,13 +68,13 @@ struct hb_font_funcs_t
 #define HB_FONT_FUNC_IMPLEMENT(name) void *name;
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
-  } user_data;
+  } *user_data;
 
   struct {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
-  } destroy;
+  } *destroy;
 
   /* Don't access these directly.  Call font->get_*() instead. */
   union get_t {
@@ -104,6 +104,8 @@ DECLARE_NULL_INSTANCE (hb_font_funcs_t);
 struct hb_font_t
 {
   hb_object_header_t header;
+  unsigned int serial;
+  unsigned int serial_coords;
 
   hb_font_t *parent;
   hb_face_t *face;
@@ -112,6 +114,8 @@ struct hb_font_t
   int32_t y_scale;
   float slant;
   float slant_xy;
+  float x_multf;
+  float y_multf;
   int64_t x_mult;
   int64_t y_mult;
 
@@ -137,12 +141,12 @@ struct hb_font_t
   { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; }
   hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); }
   hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); }
-  hb_position_t em_scalef_x (float v) { return em_scalef (v, x_scale); }
-  hb_position_t em_scalef_y (float v) { return em_scalef (v, y_scale); }
-  float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); }
-  float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); }
-  float em_fscalef_x (float v) { return em_fscalef (v, x_scale); }
-  float em_fscalef_y (float v) { return em_fscalef (v, y_scale); }
+  hb_position_t em_scalef_x (float v) { return em_multf (v, x_multf); }
+  hb_position_t em_scalef_y (float v) { return em_multf (v, y_multf); }
+  float em_fscale_x (int16_t v) { return em_fmult (v, x_multf); }
+  float em_fscale_y (int16_t v) { return em_fmult (v, y_multf); }
+  float em_fscalef_x (float v) { return em_fmultf (v, x_multf); }
+  float em_fscalef_y (float v) { return em_fmultf (v, y_multf); }
   hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
   { return em_mult (v, dir_mult (direction)); }
 
@@ -205,14 +209,14 @@ struct hb_font_t
     memset (extents, 0, sizeof (*extents));
     return klass->get.f.font_h_extents (this, user_data,
 					extents,
-					klass->user_data.font_h_extents);
+					!klass->user_data ? nullptr : klass->user_data->font_h_extents);
   }
   hb_bool_t get_font_v_extents (hb_font_extents_t *extents)
   {
     memset (extents, 0, sizeof (*extents));
     return klass->get.f.font_v_extents (this, user_data,
 					extents,
-					klass->user_data.font_v_extents);
+					!klass->user_data ? nullptr : klass->user_data->font_v_extents);
   }
 
   bool has_glyph (hb_codepoint_t unicode)
@@ -228,7 +232,7 @@ struct hb_font_t
     *glyph = not_found;
     return klass->get.f.nominal_glyph (this, user_data,
 				       unicode, glyph,
-				       klass->user_data.nominal_glyph);
+				       !klass->user_data ? nullptr : klass->user_data->nominal_glyph);
   }
   unsigned int get_nominal_glyphs (unsigned int count,
 				   const hb_codepoint_t *first_unicode,
@@ -240,7 +244,7 @@ struct hb_font_t
 					count,
 					first_unicode, unicode_stride,
 					first_glyph, glyph_stride,
-					klass->user_data.nominal_glyphs);
+					!klass->user_data ? nullptr : klass->user_data->nominal_glyphs);
   }
 
   hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector,
@@ -250,21 +254,21 @@ struct hb_font_t
     *glyph = not_found;
     return klass->get.f.variation_glyph (this, user_data,
 					 unicode, variation_selector, glyph,
-					 klass->user_data.variation_glyph);
+					 !klass->user_data ? nullptr : klass->user_data->variation_glyph);
   }
 
   hb_position_t get_glyph_h_advance (hb_codepoint_t glyph)
   {
     return klass->get.f.glyph_h_advance (this, user_data,
 					 glyph,
-					 klass->user_data.glyph_h_advance);
+					 !klass->user_data ? nullptr : klass->user_data->glyph_h_advance);
   }
 
   hb_position_t get_glyph_v_advance (hb_codepoint_t glyph)
   {
     return klass->get.f.glyph_v_advance (this, user_data,
 					 glyph,
-					 klass->user_data.glyph_v_advance);
+					 !klass->user_data ? nullptr : klass->user_data->glyph_v_advance);
   }
 
   void get_glyph_h_advances (unsigned int count,
@@ -277,7 +281,7 @@ struct hb_font_t
 					  count,
 					  first_glyph, glyph_stride,
 					  first_advance, advance_stride,
-					  klass->user_data.glyph_h_advances);
+					  !klass->user_data ? nullptr : klass->user_data->glyph_h_advances);
   }
 
   void get_glyph_v_advances (unsigned int count,
@@ -290,7 +294,7 @@ struct hb_font_t
 					  count,
 					  first_glyph, glyph_stride,
 					  first_advance, advance_stride,
-					  klass->user_data.glyph_v_advances);
+					  !klass->user_data ? nullptr : klass->user_data->glyph_v_advances);
   }
 
   hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
@@ -299,7 +303,7 @@ struct hb_font_t
     *x = *y = 0;
     return klass->get.f.glyph_h_origin (this, user_data,
 					glyph, x, y,
-					klass->user_data.glyph_h_origin);
+					!klass->user_data ? nullptr : klass->user_data->glyph_h_origin);
   }
 
   hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph,
@@ -308,7 +312,7 @@ struct hb_font_t
     *x = *y = 0;
     return klass->get.f.glyph_v_origin (this, user_data,
 					glyph, x, y,
-					klass->user_data.glyph_v_origin);
+					!klass->user_data ? nullptr : klass->user_data->glyph_v_origin);
   }
 
   hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph,
@@ -319,7 +323,7 @@ struct hb_font_t
 #else
     return klass->get.f.glyph_h_kerning (this, user_data,
 					 left_glyph, right_glyph,
-					 klass->user_data.glyph_h_kerning);
+					 !klass->user_data ? nullptr : klass->user_data->glyph_h_kerning);
 #endif
   }
 
@@ -331,7 +335,7 @@ struct hb_font_t
 #else
     return klass->get.f.glyph_v_kerning (this, user_data,
 					 top_glyph, bottom_glyph,
-					 klass->user_data.glyph_v_kerning);
+					 !klass->user_data ? nullptr : klass->user_data->glyph_v_kerning);
 #endif
   }
 
@@ -342,7 +346,7 @@ struct hb_font_t
     return klass->get.f.glyph_extents (this, user_data,
 				       glyph,
 				       extents,
-				       klass->user_data.glyph_extents);
+				       !klass->user_data ? nullptr : klass->user_data->glyph_extents);
   }
 
   hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index,
@@ -352,7 +356,7 @@ struct hb_font_t
     return klass->get.f.glyph_contour_point (this, user_data,
 					     glyph, point_index,
 					     x, y,
-					     klass->user_data.glyph_contour_point);
+					     !klass->user_data ? nullptr : klass->user_data->glyph_contour_point);
   }
 
   hb_bool_t get_glyph_name (hb_codepoint_t glyph,
@@ -362,7 +366,7 @@ struct hb_font_t
     return klass->get.f.glyph_name (this, user_data,
 				    glyph,
 				    name, size,
-				    klass->user_data.glyph_name);
+				    !klass->user_data ? nullptr : klass->user_data->glyph_name);
   }
 
   hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */
@@ -373,7 +377,7 @@ struct hb_font_t
     return klass->get.f.glyph_from_name (this, user_data,
 					 name, len,
 					 glyph,
-					 klass->user_data.glyph_from_name);
+					 !klass->user_data ? nullptr : klass->user_data->glyph_from_name);
   }
 
   void get_glyph_shape (hb_codepoint_t glyph,
@@ -382,7 +386,7 @@ struct hb_font_t
     klass->get.f.glyph_shape (this, user_data,
 			      glyph,
 			      draw_funcs, draw_data,
-			      klass->user_data.glyph_shape);
+			      !klass->user_data ? nullptr : klass->user_data->glyph_shape);
   }
 
 
@@ -444,7 +448,6 @@ struct hb_font_t
   {
     *x = get_glyph_h_advance (glyph) / 2;
 
-    /* TODO cache this somehow?! */
     hb_font_extents_t extents;
     get_h_extents_with_fallback (&extents);
     *y = extents.ascender;
@@ -628,20 +631,26 @@ struct hb_font_t
 
   void mults_changed ()
   {
-    signed upem = face->get_upem ();
-    x_mult = ((int64_t) x_scale << 16) / upem;
-    y_mult = ((int64_t) y_scale << 16) / upem;
+    float upem = face->get_upem ();
+    x_multf = x_scale / upem;
+    y_multf = y_scale / upem;
+    bool x_neg = x_scale < 0;
+    x_mult = (x_neg ? -((int64_t) -x_scale << 16) : ((int64_t) x_scale << 16)) / upem;
+    bool y_neg = y_scale < 0;
+    y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem;
     slant_xy = y_scale ? slant * x_scale / y_scale : 0.f;
+
+    data.fini ();
   }
 
   hb_position_t em_mult (int16_t v, int64_t mult)
   { return (hb_position_t) ((v * mult + 32768) >> 16); }
-  hb_position_t em_scalef (float v, int scale)
-  { return (hb_position_t) roundf (em_fscalef (v, scale)); }
-  float em_fscalef (float v, int scale)
-  { return v * scale / face->get_upem (); }
-  float em_fscale (int16_t v, int scale)
-  { return (float) v * scale / face->get_upem (); }
+  hb_position_t em_multf (float v, float mult)
+  { return (hb_position_t) roundf (em_fmultf (v, mult)); }
+  float em_fmultf (float v, float mult)
+  { return v * mult; }
+  float em_fmult (int16_t v, float mult)
+  { return (float) v * mult; }
 };
 DECLARE_NULL_INSTANCE (hb_font_t);
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
index 0cfbb22e31d18e51f4cdf3b006f1e204d851573d..b526a828657b57e596dc5aa37872d0adce15d410 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
@@ -37,6 +37,8 @@
 #include "hb-font.hh"
 #include "hb-machinery.hh"
 #include "hb-cache.hh"
+#include "hb-ot-os2-table.hh"
+#include "hb-ot-shaper-arabic-pua.hh"
 
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
@@ -80,13 +82,13 @@
 
 struct hb_ft_font_t
 {
-  mutable hb_mutex_t lock;
-  FT_Face ft_face;
   int load_flags;
   bool symbol; /* Whether selected cmap is symbol cmap. */
   bool unref; /* Whether to destroy ft_face when done. */
 
-  mutable int cached_x_scale;
+  mutable hb_mutex_t lock;
+  FT_Face ft_face;
+  mutable unsigned cached_serial;
   mutable hb_advance_cache_t advance_cache;
 };
 
@@ -103,7 +105,7 @@ _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref)
 
   ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
 
-  ft_font->cached_x_scale = 0;
+  ft_font->cached_serial = (unsigned) -1;
   ft_font->advance_cache.init ();
 
   return ft_font;
@@ -130,6 +132,58 @@ _hb_ft_font_destroy (void *data)
   hb_free (ft_font);
 }
 
+
+/* hb_font changed, update FT_Face. */
+static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
+{
+
+  FT_Set_Char_Size (ft_face,
+		    abs (font->x_scale), abs (font->y_scale),
+		    0, 0);
+#if 0
+		    font->x_ppem * 72 * 64 / font->x_scale,
+		    font->y_ppem * 72 * 64 / font->y_scale);
+#endif
+  if (font->x_scale < 0 || font->y_scale < 0)
+  {
+    FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
+			  0, font->y_scale < 0 ? -1 : +1};
+    FT_Set_Transform (ft_face, &matrix, nullptr);
+  }
+
+#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
+  unsigned int num_coords;
+  const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
+  if (num_coords)
+  {
+    FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed));
+    if (ft_coords)
+    {
+      for (unsigned int i = 0; i < num_coords; i++)
+	ft_coords[i] = coords[i] * 4;
+      FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
+      hb_free (ft_coords);
+    }
+  }
+#endif
+}
+
+/* Check if hb_font changed, update FT_Face. */
+static inline bool
+_hb_ft_hb_font_check_changed (hb_font_t *font,
+			      const hb_ft_font_t *ft_font)
+{
+  if (font->serial != ft_font->cached_serial)
+  {
+    _hb_ft_hb_font_changed (font, ft_font->ft_face);
+    ft_font->advance_cache.clear ();
+    ft_font->cached_serial = font->serial;
+    return true;
+  }
+  return false;
+}
+
+
 /**
  * hb_ft_font_set_load_flags:
  * @font: #hb_font_t to work upon
@@ -181,7 +235,7 @@ hb_ft_font_get_load_flags (hb_font_t *font)
 }
 
 /**
- * hb_ft_font_get_face:
+ * hb_ft_font_get_face: (skip)
  * @font: #hb_font_t to work upon
  *
  * Fetches the FT_Face associated with the specified #hb_font_t
@@ -203,7 +257,7 @@ hb_ft_font_get_face (hb_font_t *font)
 }
 
 /**
- * hb_ft_font_lock_face:
+ * hb_ft_font_lock_face: (skip)
  * @font: #hb_font_t to work upon
  *
  * Gets the FT_Face associated with @font, This face will be kept around until
@@ -246,7 +300,7 @@ hb_ft_font_unlock_face (hb_font_t *font)
 
 
 static hb_bool_t
-hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED,
+hb_ft_get_nominal_glyph (hb_font_t *font,
 			 void *font_data,
 			 hb_codepoint_t unicode,
 			 hb_codepoint_t *glyph,
@@ -258,14 +312,29 @@ hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED,
 
   if (unlikely (!g))
   {
-    if (unlikely (ft_font->symbol) && unicode <= 0x00FFu)
+    if (unlikely (ft_font->symbol))
     {
-      /* For symbol-encoded OpenType fonts, we duplicate the
-       * U+F000..F0FF range at U+0000..U+00FF.  That's what
-       * Windows seems to do, and that's hinted about at:
-       * https://docs.microsoft.com/en-us/typography/opentype/spec/recom
-       * under "Non-Standard (Symbol) Fonts". */
-      g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode);
+      switch ((unsigned) font->face->table.OS2->get_font_page ()) {
+      case OT::OS2::font_page_t::FONT_PAGE_NONE:
+	if (unicode <= 0x00FFu)
+	  /* For symbol-encoded OpenType fonts, we duplicate the
+	   * U+F000..F0FF range at U+0000..U+00FF.  That's what
+	   * Windows seems to do, and that's hinted about at:
+	   * https://docs.microsoft.com/en-us/typography/opentype/spec/recom
+	   * under "Non-Standard (Symbol) Fonts". */
+	  g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode);
+	break;
+#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK
+      case OT::OS2::font_page_t::FONT_PAGE_SIMP_ARABIC:
+	g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_simp_map (unicode));
+	break;
+      case OT::OS2::font_page_t::FONT_PAGE_TRAD_ARABIC:
+	g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_trad_map (unicode));
+	break;
+#endif
+      default:
+	break;
+      }
       if (!g)
 	return false;
     }
@@ -337,12 +406,6 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
   int load_flags = ft_font->load_flags;
   int mult = font->x_scale < 0 ? -1 : +1;
 
-  if (font->x_scale != ft_font->cached_x_scale)
-  {
-    ft_font->advance_cache.clear ();
-    ft_font->cached_x_scale = font->x_scale;
-  }
-
   for (unsigned int i = 0; i < count; i++)
   {
     FT_Fixed v = 0;
@@ -426,6 +489,7 @@ hb_ft_get_glyph_h_kerning (hb_font_t *font,
 			   void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Vector kerningv;
 
   FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED;
@@ -556,6 +620,7 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED,
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
+
   metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale);
   metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale);
   metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender);
@@ -619,6 +684,8 @@ hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED,
   hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
+  _hb_ft_hb_font_check_changed (font, ft_font);
+
   if (unlikely (FT_Load_Glyph (ft_face, glyph,
 			       FT_LOAD_NO_BITMAP | ft_font->load_flags)))
     return;
@@ -963,6 +1030,31 @@ hb_ft_font_changed (hb_font_t *font)
 #endif
 }
 
+/**
+ * hb_ft_hb_font_changed:
+ * @font: #hb_font_t to work upon
+ *
+ * Refreshes the state of the underlying FT_Face of @font when the hb_font_t
+ * @font has changed.
+ * This function should be called after changing the size or
+ * variation-axis settings on the @font.
+ * This call is fast if nothing has changed on @font.
+ *
+ * Return value: true if changed, false otherwise
+ *
+ * Since: 4.4.0
+ **/
+hb_bool_t
+hb_ft_hb_font_changed (hb_font_t *font)
+{
+  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+    return false;
+
+  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
+
+  return _hb_ft_hb_font_check_changed (font, ft_font);
+}
+
 /**
  * hb_ft_font_create_referenced:
  * @ft_face: FT_Face to work upon
@@ -1081,35 +1173,7 @@ hb_ft_font_set_funcs (hb_font_t *font)
   if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL))
     FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
 
-  FT_Set_Char_Size (ft_face,
-		    abs (font->x_scale), abs (font->y_scale),
-		    0, 0);
-#if 0
-		    font->x_ppem * 72 * 64 / font->x_scale,
-		    font->y_ppem * 72 * 64 / font->y_scale);
-#endif
-  if (font->x_scale < 0 || font->y_scale < 0)
-  {
-    FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
-			  0, font->y_scale < 0 ? -1 : +1};
-    FT_Set_Transform (ft_face, &matrix, nullptr);
-  }
-
-#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
-  unsigned int num_coords;
-  const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
-  if (num_coords)
-  {
-    FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed));
-    if (ft_coords)
-    {
-      for (unsigned int i = 0; i < num_coords; i++)
-	ft_coords[i] = coords[i] * 4;
-      FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
-      hb_free (ft_coords);
-    }
-  }
-#endif
+  _hb_ft_hb_font_changed (font, ft_face);
 
   ft_face->generic.data = blob;
   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h
index bf07115ab92fefb65f2609d1149bdf15a2c66318..6a8a7abe8cc670cefdd2580d7ca54d506d1762aa 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h
@@ -122,10 +122,17 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags);
 HB_EXTERN int
 hb_ft_font_get_load_flags (hb_font_t *font);
 
-/* Call when size or variations settings on underlying FT_Face change. */
+/* Call when size or variations settings on underlying FT_Face changed,
+ * and you want to update the hb_font_t from it. */
 HB_EXTERN void
 hb_ft_font_changed (hb_font_t *font);
 
+/* Call when size or variations settings on underlying hb_font_t may have
+ * changed, and you want to update the FT_Face from it.  This call is fast
+ * if nothing changed on hb_font_t. Returns true if changed. */
+HB_EXTERN hb_bool_t
+hb_ft_hb_font_changed (hb_font_t *font);
+
 /* Makes an hb_font_t use FreeType internally to implement font functions.
  * Note: this internally creates an FT_Face.  Use it when you create your
  * hb_face_t using hb_face_create(). */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-gobject-enums.h.tmpl b/source/libs/harfbuzz/harfbuzz-src/src/hb-gobject-enums.h.tmpl
index a8467868bdbb3dc45743b2b3e44a8c484b065f11..6dd98f7fed68b7bcca5ce15ab6ed12938c1074d1 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-gobject-enums.h.tmpl
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-gobject-enums.h.tmpl
@@ -43,7 +43,7 @@ HB_BEGIN_DECLS
 
 /*** BEGIN value-header ***/
 HB_EXTERN GType
-@enum_name@_get_type () G_GNUC_CONST;
+@enum_name@_get_type (void) G_GNUC_CONST;
 #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
 
 /*** END value-header ***/
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-iter.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-iter.hh
index 43a3098f65e24f6bb8167c096307a637a4b343ab..d4461f166cc5395360689b2a21a53e9916de1ad0 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-iter.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-iter.hh
@@ -43,17 +43,12 @@
  * is writable, then the iterator returns lvalues, otherwise it
  * returns rvalues.
  *
- * TODO Document more.
- *
- * If iterator implementation implements operator!=, then can be
+ * If iterator implementation implements operator!=, then it can be
  * used in range-based for loop.  That already happens if the iterator
  * is random-access.  Otherwise, the range-based for loop incurs
  * one traversal to find end(), which can be avoided if written
  * as a while-style for loop, or if iterator implements a faster
- * __end__() method.
- * TODO When opting in for C++17, address this by changing return
- * type of .end()?
- */
+ * __end__() method. */
 
 /*
  * Base classes for iterators.
@@ -75,10 +70,6 @@ struct hb_iter_t
 	iter_t* thiz ()       { return static_cast<      iter_t *> (this); }
   public:
 
-  /* TODO:
-   * Port operators below to use hb_enable_if to sniff which method implements
-   * an operator and use it, and remove hb_iter_fallback_mixin_t completely. */
-
   /* Operators. */
   iter_t iter () const { return *thiz(); }
   iter_t operator + () const { return *thiz(); }
@@ -87,8 +78,7 @@ struct hb_iter_t
   explicit operator bool () const { return thiz()->__more__ (); }
   unsigned len () const { return thiz()->__len__ (); }
   /* The following can only be enabled if item_t is reference type.  Otherwise
-   * it will be returning pointer to temporary rvalue.
-   * TODO Use a wrapper return type to fix for non-reference type. */
+   * it will be returning pointer to temporary rvalue. */
   template <typename T = item_t,
 	    hb_enable_if (std::is_reference<T>::value)>
   hb_remove_reference<item_t>* operator -> () const { return std::addressof (**thiz()); }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh
index e52a6a41240d221211843b5d8d17471d1b244130..ff2a99f5edc9c6d295c1f71c2c4efb1f4584e72c 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh
@@ -176,7 +176,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData>
 
   void init0 () {} /* Init, when memory is already set to 0. No-op for us. */
   void init ()  { instance.set_relaxed (nullptr); }
-  void fini ()  { do_destroy (instance.get ()); }
+  void fini ()  { do_destroy (instance.get ()); init (); }
 
   void free_instance ()
   {
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc
index 9f1ac42846da1b0685385396ca958b8ee400054d..089615c72a0dab46008efd8305aaa355ad1a08c5 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc
@@ -40,7 +40,7 @@
 
 
 /**
- * hb_map_create: (Xconstructor)
+ * hb_map_create:
  *
  * Creates a new, initially empty map.
  *
@@ -172,6 +172,25 @@ hb_map_allocation_successful (const hb_map_t  *map)
   return map->successful;
 }
 
+/**
+ * hb_map_copy:
+ * @map: A map
+ *
+ * Allocate a copy of @map.
+ *
+ * Return value: Newly-allocated map.
+ *
+ * Since: 4.4.0
+ **/
+hb_map_t *
+hb_map_copy (const hb_map_t *map)
+{
+  hb_map_t *copy = hb_map_create ();
+  if (unlikely (!copy)) return nullptr;
+  copy->resize (map->population);
+  hb_copy (*map, *copy);
+  return copy;
+}
 
 /**
  * hb_map_set:
@@ -289,3 +308,40 @@ hb_map_get_population (const hb_map_t *map)
 {
   return map->get_population ();
 }
+
+/**
+ * hb_map_is_equal:
+ * @map: A map
+ * @other: Another map
+ *
+ * Tests whether @map and @other are equal (contain the same
+ * elements).
+ *
+ * Return value: %true if the two maps are equal, %false otherwise.
+ *
+ * Since: 4.3.0
+ **/
+hb_bool_t
+hb_map_is_equal (const hb_map_t *map,
+		 const hb_map_t *other)
+{
+  return map->is_equal (*other);
+}
+
+/**
+ * hb_map_hash:
+ * @map: A map
+ *
+ * Creates a hash representing @map.
+ *
+ * Return value:
+ * A hash of @map.
+ *
+ * Since: 4.4.0
+ **/
+HB_EXTERN unsigned int
+hb_map_hash (const hb_map_t *map)
+{
+  return map->hash ();
+}
+
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h
index 6a45a7bdd52411d2e7b00a63216844e36e7b2247..41d877d6a15b49f3924f90216e5fa7c67b5b388a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h
@@ -82,6 +82,9 @@ hb_map_get_user_data (hb_map_t           *map,
 HB_EXTERN hb_bool_t
 hb_map_allocation_successful (const hb_map_t *map);
 
+HB_EXTERN hb_map_t *
+hb_map_copy (const hb_map_t *map);
+
 HB_EXTERN void
 hb_map_clear (hb_map_t *map);
 
@@ -91,6 +94,13 @@ hb_map_is_empty (const hb_map_t *map);
 HB_EXTERN unsigned int
 hb_map_get_population (const hb_map_t *map);
 
+HB_EXTERN hb_bool_t
+hb_map_is_equal (const hb_map_t *map,
+		 const hb_map_t *other);
+
+HB_EXTERN unsigned int
+hb_map_hash (const hb_map_t *map);
+
 HB_EXTERN void
 hb_map_set (hb_map_t       *map,
 	    hb_codepoint_t  key,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh
index 9341637eac041a900c3b1640deb249b8ef1bbc28..5efad8d20cd2daeb6cb3b80da1e5d497355d2c35 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh
@@ -34,19 +34,18 @@
  * hb_hashmap_t
  */
 
+extern HB_INTERNAL const hb_codepoint_t minus_1;
+
 template <typename K, typename V,
-	  typename k_invalid_t = K,
-	  typename v_invalid_t = V,
-	  k_invalid_t kINVALID = std::is_pointer<K>::value ? 0 : std::is_signed<K>::value ? hb_int_min (K) : (K) -1,
-	  v_invalid_t vINVALID = std::is_pointer<V>::value ? 0 : std::is_signed<V>::value ? hb_int_min (V) : (V) -1>
+	  bool minus_one = false>
 struct hb_hashmap_t
 {
   hb_hashmap_t ()  { init (); }
   ~hb_hashmap_t () { fini (); }
 
-  hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { hb_copy (o, *this); }
+  hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (population); hb_copy (o, *this); }
   hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); }
-  hb_hashmap_t& operator= (const hb_hashmap_t& o)  { hb_copy (o, *this); return *this; }
+  hb_hashmap_t& operator= (const hb_hashmap_t& o)  { resize (population); hb_copy (o, *this); return *this; }
   hb_hashmap_t& operator= (hb_hashmap_t&& o)  { hb_swap (*this, o); return *this; }
 
   hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t ()
@@ -58,44 +57,53 @@ struct hb_hashmap_t
 	    hb_requires (hb_is_iterable (Iterable))>
   hb_hashmap_t (const Iterable &o) : hb_hashmap_t ()
   {
-    hb_copy (o, *this);
+    auto iter = hb_iter (o);
+    if (iter.is_random_access_iterator)
+      resize (hb_len (iter));
+    hb_copy (iter, *this);
   }
 
   struct item_t
   {
     K key;
+    uint32_t hash : 30;
+    uint32_t is_used_ : 1;
+    uint32_t is_tombstone_ : 1;
     V value;
-    uint32_t hash;
+
+    bool is_used () const { return is_used_; }
+    void set_used (bool is_used) { is_used_ = is_used; }
+    bool is_tombstone () const { return is_tombstone_; }
+    void set_tombstone (bool is_tombstone) { is_tombstone_ = is_tombstone; }
+    bool is_real () const { return is_used_ && !is_tombstone_; }
+
+    template <bool v = minus_one,
+	      hb_enable_if (v == false)>
+    static inline const V& default_value () { return Null(V); };
+    template <bool v = minus_one,
+	      hb_enable_if (v == true)>
+    static inline const V& default_value ()
+    {
+      static_assert (hb_is_same (V, hb_codepoint_t), "");
+      return minus_1;
+    };
 
     void clear ()
     {
       new (std::addressof (key)) K ();
-      key = hb_coerce<K> (kINVALID);
       new (std::addressof (value)) V ();
-      value = hb_coerce<V> (vINVALID);
       hash = 0;
+      is_used_ = false;
+      is_tombstone_ = false;
     }
 
     bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); }
     bool operator == (const item_t &o) { return *this == o.key; }
-    bool is_unused () const
-    {
-      const K inv = hb_coerce<K> (kINVALID);
-      return key == inv;
-    }
-    bool is_tombstone () const
-    {
-      const K kinv = hb_coerce<K> (kINVALID);
-      const V vinv = hb_coerce<V> (vINVALID);
-      return key != kinv && value == vinv;
-    }
-    bool is_real () const
-    {
-      const K kinv = hb_coerce<K> (kINVALID);
-      const V vinv = hb_coerce<V> (vINVALID);
-      return key != kinv && value != vinv;
-    }
     hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); }
+    hb_pair_t<const K &, const V &> get_pair_ref() const { return hb_pair_t<const K &, const V &> (key, value); }
+
+    uint32_t total_hash () const
+    { return (hash * 31) + hb_hash (value); }
   };
 
   hb_object_header_t header;
@@ -154,11 +162,11 @@ struct hb_hashmap_t
 
   bool in_error () const { return !successful; }
 
-  bool resize ()
+  bool resize (unsigned new_population = 0)
   {
     if (unlikely (!successful)) return false;
 
-    unsigned int power = hb_bit_storage (population * 2 + 8);
+    unsigned int power = hb_bit_storage (hb_max (population, new_population) * 2 + 8);
     unsigned int new_size = 1u << power;
     item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t));
     if (unlikely (!new_items))
@@ -196,27 +204,39 @@ struct hb_hashmap_t
     return true;
   }
 
-  bool set (K key, const V& value) { return set_with_hash (key, hb_hash (key), value); }
-  bool set (K key, V&& value) { return set_with_hash (key, hb_hash (key), std::move (value)); }
+  template <typename VV>
+  bool set (K key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value)); }
 
-  V get (K key) const
+  const V& get (K key) const
   {
-    if (unlikely (!items)) return hb_coerce<V> (vINVALID);
+    if (unlikely (!items)) return item_t::default_value ();
     unsigned int i = bucket_for (key);
-    return items[i].is_real () && items[i] == key ? items[i].value : hb_coerce<V> (vINVALID);
+    return items[i].is_real () && items[i] == key ? items[i].value : item_t::default_value ();
   }
 
-  void del (K key) { set (key, hb_coerce<V> (vINVALID)); }
+  void del (K key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); }
 
   /* Has interface. */
-  typedef V value_t;
+  typedef const V& value_t;
   value_t operator [] (K k) const { return get (k); }
-  bool has (K k, V *vp = nullptr) const
+  bool has (K key, const V **vp = nullptr) const
   {
-    V v = (*this)[k];
-    if (vp) *vp = v;
-    const V vinv = hb_coerce<V> (vINVALID);
-    return v != vinv;
+    if (unlikely (!items))
+    {
+      if (vp) *vp = &item_t::default_value ();
+      return false;
+    }
+    unsigned int i = bucket_for (key);
+    if (items[i].is_real () && items[i] == key)
+    {
+      if (vp) *vp = &items[i].value;
+      return true;
+    }
+    else
+    {
+      if (vp) *vp = &item_t::default_value ();
+      return false;
+    }
   }
   /* Projection. */
   V operator () (K k) const { return get (k); }
@@ -235,6 +255,28 @@ struct hb_hashmap_t
   bool is_empty () const { return population == 0; }
   explicit operator bool () const { return !is_empty (); }
 
+  uint32_t hash () const
+  {
+    uint32_t h = 0;
+    for (const auto &item : + hb_array (items, mask ? mask + 1 : 0)
+			    | hb_filter (&item_t::is_real))
+      h ^= item.total_hash ();
+    return h;
+  }
+
+  bool is_equal (const hb_hashmap_t &other) const
+  {
+    if (population != other.population) return false;
+
+    for (auto pair : iter ())
+      if (get (pair.first) != pair.second)
+        return false;
+
+    return true;
+  }
+  bool operator == (const hb_hashmap_t &other) const { return is_equal (other); }
+  bool operator != (const hb_hashmap_t &other) const { return !is_equal (other); }
+
   unsigned int get_population () const { return population; }
 
   /*
@@ -246,6 +288,12 @@ struct hb_hashmap_t
     | hb_filter (&item_t::is_real)
     | hb_map (&item_t::get_pair)
   )
+  auto iter_ref () const HB_AUTO_RETURN
+  (
+    + hb_array (items, mask ? mask + 1 : 0)
+    | hb_filter (&item_t::is_real)
+    | hb_map (&item_t::get_pair_ref)
+  )
   auto keys () const HB_AUTO_RETURN
   (
     + hb_array (items, mask ? mask + 1 : 0)
@@ -268,19 +316,16 @@ struct hb_hashmap_t
   protected:
 
   template <typename VV>
-  bool set_with_hash (K key, uint32_t hash, VV&& value)
+  bool set_with_hash (K key, uint32_t hash, VV&& value, bool is_delete=false)
   {
     if (unlikely (!successful)) return false;
-    const K kinv = hb_coerce<K> (kINVALID);
-    if (unlikely (key == kinv)) return true;
     if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false;
     unsigned int i = bucket_for_hash (key, hash);
 
-    const V vinv = hb_coerce<V> (vINVALID);
-    if (value == vinv && items[i].key != key)
+    if (is_delete && items[i].key != key)
       return true; /* Trying to delete non-existent key. */
 
-    if (!items[i].is_unused ())
+    if (items[i].is_used ())
     {
       occupancy--;
       if (!items[i].is_tombstone ())
@@ -288,27 +333,30 @@ struct hb_hashmap_t
     }
 
     items[i].key = key;
-    items[i].value = value;
+    items[i].value = std::forward<VV> (value);
     items[i].hash = hash;
+    items[i].set_used (true);
+    items[i].set_tombstone (is_delete);
 
     occupancy++;
-    if (!items[i].is_tombstone ())
+    if (!is_delete)
       population++;
 
     return true;
   }
 
-  unsigned int bucket_for (K key) const
+  unsigned int bucket_for (const K &key) const
   {
     return bucket_for_hash (key, hb_hash (key));
   }
 
-  unsigned int bucket_for_hash (K key, uint32_t hash) const
+  unsigned int bucket_for_hash (const K &key, uint32_t hash) const
   {
+    hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
     unsigned int i = hash % prime;
     unsigned int step = 0;
     unsigned int tombstone = (unsigned) -1;
-    while (!items[i].is_unused ())
+    while (items[i].is_used ())
     {
       if (items[i].hash == hash && items[i] == key)
 	return i;
@@ -377,21 +425,16 @@ struct hb_hashmap_t
 
 struct hb_map_t : hb_hashmap_t<hb_codepoint_t,
 			       hb_codepoint_t,
-			       hb_codepoint_t,
-			       hb_codepoint_t,
-			       HB_MAP_VALUE_INVALID,
-			       HB_MAP_VALUE_INVALID>
+			       true>
 {
   using hashmap = hb_hashmap_t<hb_codepoint_t,
 			       hb_codepoint_t,
-			       hb_codepoint_t,
-			       hb_codepoint_t,
-			       HB_MAP_VALUE_INVALID,
-			       HB_MAP_VALUE_INVALID>;
+			       true>;
 
-  hb_map_t () = default;
   ~hb_map_t () = default;
-  hb_map_t (hb_map_t&) = default;
+  hb_map_t () : hashmap () {}
+  hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {}
+  hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {}
   hb_map_t& operator= (const hb_map_t&) = default;
   hb_map_t& operator= (hb_map_t&&) = default;
   hb_map_t (std::initializer_list<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> lst) : hashmap (lst) {}
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh
index 3fea5d995e9da3656996690acc584fe2c07ec31c..e97d790fc362d7bb26570bd09000fe14aaa7ed6f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh
@@ -188,6 +188,19 @@ template <> struct hb_int_max<signed long long>		: hb_integral_constant<signed l
 template <> struct hb_int_max<unsigned long long>	: hb_integral_constant<unsigned long long,	ULLONG_MAX>	{};
 #define hb_int_max(T) hb_int_max<T>::value
 
+#if defined(__GNUC__) && __GNUC__ < 5
+#define hb_is_trivially_copyable(T) __has_trivial_copy(T)
+#define hb_is_trivially_copy_assignable(T) __has_trivial_assign(T)
+#define hb_is_trivially_constructible(T) __has_trivial_constructor(T)
+#define hb_is_trivially_copy_constructible(T) __has_trivial_copy_constructor(T)
+#define hb_is_trivially_destructible(T) __has_trivial_destructor(T)
+#else
+#define hb_is_trivially_copyable(T) std::is_trivially_copyable<T>::value
+#define hb_is_trivially_copy_assignable(T) std::is_trivially_copy_assignable<T>::value
+#define hb_is_trivially_constructible(T) std::is_trivially_constructible<T>::value
+#define hb_is_trivially_copy_constructible(T) std::is_trivially_copy_constructible<T>::value
+#define hb_is_trivially_destructible(T) std::is_trivially_destructible<T>::value
+#endif
 
 /* Class traits. */
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-null.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-null.hh
index db38a4dfd24195ba810e9cfbb0e7126fbed24b1b..78eb6474d577e08b527b6fc05cd9dccd36eee527 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-null.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-null.hh
@@ -37,7 +37,7 @@
 
 /* Global nul-content Null pool.  Enlarge as necessary. */
 
-#define HB_NULL_POOL_SIZE 384
+#define HB_NULL_POOL_SIZE 448
 
 /* Use SFINAE to sniff whether T has min_size; in which case return the larger
  * of sizeof(T) and T::null_size, otherwise return sizeof(T).
@@ -108,7 +108,7 @@ struct NullHelper
 /* Specializations for arbitrary-content Null objects expressed in bytes. */
 #define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \
 	} /* Close namespace. */ \
-	extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]; \
+	extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[hb_null_size (Namespace::Type)]; \
 	template <> \
 	struct Null<Namespace::Type> { \
 	  static Namespace::Type const & get_null () { \
@@ -118,7 +118,7 @@ struct NullHelper
 	namespace Namespace { \
 	static_assert (true, "") /* Require semicolon after. */
 #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \
-	const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]
+	const unsigned char _hb_Null_##Namespace##_##Type[hb_null_size (Namespace::Type)]
 
 /* Specializations for arbitrary-content Null objects expressed as struct initializer. */
 #define DECLARE_NULL_INSTANCE(Type) \
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh
index 7e524177f6705158982e0c84d3d7d451d8e159b1..aee7064be3ee905333e78b90b8135c7dfc62a804 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh
@@ -33,6 +33,7 @@
 #include "hb-blob.hh"
 #include "hb-face.hh"
 #include "hb-machinery.hh"
+#include "hb-meta.hh"
 #include "hb-subset.hh"
 
 
@@ -518,7 +519,7 @@ struct UnsizedArrayOf
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
 	return_trace (false);
@@ -707,7 +708,7 @@ struct ArrayOf
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
@@ -835,7 +836,7 @@ struct HeadlessArrayOf
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = get_length ();
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
@@ -884,7 +885,7 @@ struct ArrayOfM1
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = lenM1 + 1;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
@@ -1070,7 +1071,7 @@ struct VarSizedBinSearchArrayOf
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = get_length ();
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!(*this)[i].sanitize (c, std::forward<Ts> (ds)...)))
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh
index c102c15173301b5ea1187e3fa1709da774d0b7e9..ae3b83a25636408deb841708a2d4b40c8e9743b7 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh
@@ -46,49 +46,21 @@ template<typename Type>
 static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset)
 { return offset ? StructAtOffset<Type> (P, offset) : Null (Type); }
 
-inline unsigned int calcOffSize (unsigned int dataSize)
-{
-  unsigned int size = 1;
-  unsigned int offset = dataSize + 1;
-  while (offset & ~0xFF)
-  {
-    size++;
-    offset >>= 8;
-  }
-  /* format does not support size > 4; caller should handle it as an error */
-  return size;
-}
-
 struct code_pair_t
 {
   hb_codepoint_t code;
   hb_codepoint_t glyph;
 };
 
-typedef hb_vector_t<unsigned char> str_buff_t;
-struct str_buff_vec_t : hb_vector_t<str_buff_t>
-{
-  unsigned int total_size () const
-  {
-    unsigned int size = 0;
-    for (unsigned int i = 0; i < length; i++)
-      size += (*this)[i].length;
-    return size;
-  }
-
-  private:
-  typedef hb_vector_t<str_buff_t> SUPER;
-};
+using str_buff_t = hb_vector_t<unsigned char>;
+using str_buff_vec_t = hb_vector_t<str_buff_t>;
 
 /* CFF INDEX */
 template <typename COUNT>
 struct CFFIndex
 {
-  static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
-  { return offSize * (count + 1); }
-
   unsigned int offset_array_size () const
-  { return calculate_offset_array_size (offSize, count); }
+  { return offSize * (count + 1); }
 
   CFFIndex *copy (hb_serialize_context_t *c) const
   {
@@ -100,55 +72,46 @@ struct CFFIndex
     return_trace (out);
   }
 
-  bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
-  {
-    TRACE_SERIALIZE (this);
-    unsigned int size = src.get_size ();
-    CFFIndex *dest = c->allocate_size<CFFIndex> (size);
-    if (unlikely (!dest)) return_trace (false);
-    memcpy (dest, &src, size);
-    return_trace (true);
-  }
-
   bool serialize (hb_serialize_context_t *c,
 		  unsigned int offSize_,
 		  const byte_str_array_t &byteArray)
   {
     TRACE_SERIALIZE (this);
+
     if (byteArray.length == 0)
     {
       COUNT *dest = c->allocate_min<COUNT> ();
       if (unlikely (!dest)) return_trace (false);
       *dest = 0;
+      return_trace (true);
     }
-    else
-    {
-      /* serialize CFFIndex header */
-      if (unlikely (!c->extend_min (this))) return_trace (false);
-      this->count = byteArray.length;
-      this->offSize = offSize_;
-      if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
-	return_trace (false);
 
-      /* serialize indices */
-      unsigned int  offset = 1;
-      unsigned int  i = 0;
-      for (; i < byteArray.length; i++)
-      {
-	set_offset_at (i, offset);
-	offset += byteArray[i].get_size ();
-      }
+    /* serialize CFFIndex header */
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+    this->count = byteArray.length;
+    this->offSize = offSize_;
+    if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
+      return_trace (false);
+
+    /* serialize indices */
+    unsigned int  offset = 1;
+    unsigned int  i = 0;
+    for (; i < byteArray.length; i++)
+    {
       set_offset_at (i, offset);
+      offset += byteArray[i].get_size ();
+    }
+    set_offset_at (i, offset);
 
-      /* serialize data */
-      for (unsigned int i = 0; i < byteArray.length; i++)
-      {
-	const byte_str_t &bs = byteArray[i];
-	unsigned char *dest = c->allocate_size<unsigned char> (bs.length);
-	if (unlikely (!dest)) return_trace (false);
-	memcpy (dest, &bs[0], bs.length);
-      }
+    /* serialize data */
+    for (unsigned int i = 0; i < byteArray.length; i++)
+    {
+      const hb_ubytes_t &bs = byteArray[i];
+      unsigned char *dest = c->allocate_size<unsigned char> (bs.length);
+      if (unlikely (!dest)) return_trace (false);
+      memcpy (dest, &bs[0], bs.length);
     }
+
     return_trace (true);
   }
 
@@ -160,7 +123,7 @@ struct CFFIndex
     byteArray.init ();
     byteArray.resize (buffArray.length);
     for (unsigned int i = 0; i < byteArray.length; i++)
-      byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length);
+      byteArray[i] = hb_ubytes_t (buffArray[i].arrayZ, buffArray[i].length);
     bool result = this->serialize (c, offSize_, byteArray);
     byteArray.fini ();
     return result;
@@ -172,18 +135,9 @@ struct CFFIndex
 		  Iterator it)
   {
     TRACE_SERIALIZE (this);
-    if (it.len () == 0)
-    {
-      COUNT *dest = c->allocate_min<COUNT> ();
-      if (unlikely (!dest)) return_trace (false);
-      *dest = 0;
-    }
-    else
-    {
-      serialize_header(c, + it | hb_map ([] (const byte_str_t &_) { return _.length; }));
-      for (const auto &_ : +it)
-	_.copy (c);
-    }
+    serialize_header(c, + it | hb_map ([] (const hb_ubytes_t &_) { return _.length; }));
+    for (const auto &_ : +it)
+      _.copy (c);
     return_trace (true);
   }
 
@@ -196,7 +150,7 @@ struct CFFIndex
   {
     auto it =
     + hb_iter (buffArray)
-    | hb_map ([] (const str_buff_t &_) { return byte_str_t (_.arrayZ, _.length); })
+    | hb_map ([] (const str_buff_t &_) { return hb_ubytes_t (_.arrayZ, _.length); })
     ;
     return serialize (c, it);
   }
@@ -209,13 +163,15 @@ struct CFFIndex
     TRACE_SERIALIZE (this);
 
     unsigned total = + it | hb_reduce (hb_add, 0);
-    unsigned off_size = calcOffSize (total);
+    unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8;
 
     /* serialize CFFIndex header */
     if (unlikely (!c->extend_min (this))) return_trace (false);
     this->count = it.len ();
+    if (!this->count) return_trace (true);
+    if (unlikely (!c->extend (this->offSize))) return_trace (false);
     this->offSize = off_size;
-    if (unlikely (!c->allocate_size<HBUINT8> (off_size * (it.len () + 1))))
+    if (unlikely (!c->allocate_size<HBUINT8> (off_size * (this->count + 1))))
       return_trace (false);
 
     /* serialize indices */
@@ -233,6 +189,7 @@ struct CFFIndex
 
   void set_offset_at (unsigned int index, unsigned int offset)
   {
+    assert (index <= count);
     HBUINT8 *p = offsets + offSize * index + offSize;
     unsigned int size = offSize;
     for (; size; size--)
@@ -243,11 +200,13 @@ struct CFFIndex
     }
   }
 
+  private:
   unsigned int offset_at (unsigned int index) const
   {
     assert (index <= count);
-    const HBUINT8 *p = offsets + offSize * index;
+
     unsigned int size = offSize;
+    const HBUINT8 *p = offsets + size * index;
     unsigned int offset = 0;
     for (; size; size--)
       offset = (offset << 8) + *p++;
@@ -256,72 +215,57 @@ struct CFFIndex
 
   unsigned int length_at (unsigned int index) const
   {
-    if (unlikely ((offset_at (index + 1) < offset_at (index)) ||
-		  (offset_at (index + 1) > offset_at (count))))
+    unsigned offset0 = offset_at (index);
+    unsigned offset1 = offset_at (index + 1);
+    if (unlikely (offset1 < offset0 || offset1 > offset_at (count)))
       return 0;
-    return offset_at (index + 1) - offset_at (index);
+    return offset1 - offset0;
   }
 
   const unsigned char *data_base () const
-  { return (const unsigned char *) this + min_size + offset_array_size (); }
-
-  unsigned int data_size () const { return HBINT8::static_size; }
+  { return (const unsigned char *) this + min_size + offSize.static_size + offset_array_size (); }
+  public:
 
-  byte_str_t operator [] (unsigned int index) const
+  hb_ubytes_t operator [] (unsigned int index) const
   {
-    if (unlikely (index >= count)) return Null (byte_str_t);
-    return byte_str_t (data_base () + offset_at (index) - 1, length_at (index));
+    if (unlikely (index >= count)) return hb_ubytes_t ();
+    unsigned length = length_at (index);
+    if (unlikely (!length)) return hb_ubytes_t ();
+    return hb_ubytes_t (data_base () + offset_at (index) - 1, length);
   }
 
   unsigned int get_size () const
   {
-    if (this == &Null (CFFIndex)) return 0;
-    if (count > 0)
-      return min_size + offset_array_size () + (offset_at (count) - 1);
-    return count.static_size;  /* empty CFFIndex contains count only */
+    if (count)
+      return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1);
+    return min_size;  /* empty CFFIndex contains count only */
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */
-			  (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
-			   c->check_array (offsets, offSize, count + 1) &&
-			   c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1))));
-  }
-
-  protected:
-  unsigned int max_offset () const
-  {
-    unsigned int max = 0;
-    for (unsigned int i = 0; i < count + 1u; i++)
-    {
-      unsigned int off = offset_at (i);
-      if (off > max) max = off;
-    }
-    return max;
+    return_trace (likely (c->check_struct (this) &&
+			  (count == 0 || /* empty INDEX */
+			   (count < count + 1u &&
+			    c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 &&
+			    c->check_array (offsets, offSize, count + 1u) &&
+			    c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count) - 1)))));
   }
 
   public:
   COUNT		count;		/* Number of object data. Note there are (count+1) offsets */
+  private:
   HBUINT8	offSize;	/* The byte size of each offset in the offsets array. */
   HBUINT8	offsets[HB_VAR_ARRAY];
 				/* The array of (count + 1) offsets into objects array (1-base). */
   /* HBUINT8 data[HB_VAR_ARRAY];	Object data */
   public:
-  DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
+  DEFINE_SIZE_MIN (COUNT::static_size);
 };
 
 template <typename COUNT, typename TYPE>
 struct CFFIndexOf : CFFIndex<COUNT>
 {
-  const byte_str_t operator [] (unsigned int index) const
-  {
-    if (likely (index < CFFIndex<COUNT>::count))
-      return byte_str_t (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
-    return Null (byte_str_t);
-  }
-
   template <typename DATA, typename PARAM1, typename PARAM2>
   bool serialize (hb_serialize_context_t *c,
 		  unsigned int offSize_,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc
index df4554ac001ce0acbbfeba523b1933fab24848c9..bd9fe5d6d408f5da0c3f88552deda84162f9605a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc
@@ -311,10 +311,8 @@ struct bounds_t
 
 struct cff1_extents_param_t
 {
-  void init (const OT::cff1::accelerator_t *_cff)
+  cff1_extents_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff)
   {
-    path_open = false;
-    cff = _cff;
     bounds.init ();
   }
 
@@ -322,7 +320,7 @@ struct cff1_extents_param_t
   void end_path     ()       { path_open = false; }
   bool is_path_open () const { return path_open; }
 
-  bool path_open;
+  bool path_open = false;
   bounds_t bounds;
 
   const OT::cff1::accelerator_t *cff;
@@ -395,12 +393,11 @@ bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, boun
   if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
 
   unsigned int fd = cff->fdSelect->get_fd (glyph);
-  cff1_cs_interpreter_t<cff1_cs_opset_extents_t, cff1_extents_param_t> interp;
-  const byte_str_t str = (*cff->charStrings)[glyph];
-  interp.env.init (str, *cff, fd);
-  interp.env.set_in_seac (in_seac);
-  cff1_extents_param_t  param;
-  param.init (cff);
+  const hb_ubytes_t str = (*cff->charStrings)[glyph];
+  cff1_cs_interp_env_t env (str, *cff, fd);
+  env.set_in_seac (in_seac);
+  cff1_cs_interpreter_t<cff1_cs_opset_extents_t, cff1_extents_param_t> interp (env);
+  cff1_extents_param_t param (cff);
   if (unlikely (!interp.interpret (param))) return false;
   bounds = param.bounds;
   return true;
@@ -541,10 +538,10 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
   if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
 
   unsigned int fd = cff->fdSelect->get_fd (glyph);
-  cff1_cs_interpreter_t<cff1_cs_opset_path_t, cff1_path_param_t> interp;
-  const byte_str_t str = (*cff->charStrings)[glyph];
-  interp.env.init (str, *cff, fd);
-  interp.env.set_in_seac (in_seac);
+  const hb_ubytes_t str = (*cff->charStrings)[glyph];
+  cff1_cs_interp_env_t env (str, *cff, fd);
+  env.set_in_seac (in_seac);
+  cff1_cs_interpreter_t<cff1_cs_opset_path_t, cff1_path_param_t> interp (env);
   cff1_path_param_t param (cff, font, draw_session, delta);
   if (unlikely (!interp.interpret (param))) return false;
 
@@ -566,18 +563,13 @@ bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, h
 
 struct get_seac_param_t
 {
-  void init (const OT::cff1::accelerator_t *_cff)
-  {
-    cff = _cff;
-    base = 0;
-    accent = 0;
-  }
+  get_seac_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) {}
 
   bool has_seac () const { return base && accent; }
 
   const OT::cff1::accelerator_t *cff;
-  hb_codepoint_t  base;
-  hb_codepoint_t  accent;
+  hb_codepoint_t  base = 0;
+  hb_codepoint_t  accent = 0;
 };
 
 struct cff1_cs_opset_seac_t : cff1_cs_opset_t<cff1_cs_opset_seac_t, get_seac_param_t>
@@ -598,11 +590,10 @@ bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_code
   if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
 
   unsigned int fd = fdSelect->get_fd (glyph);
-  cff1_cs_interpreter_t<cff1_cs_opset_seac_t, get_seac_param_t> interp;
-  const byte_str_t str = (*charStrings)[glyph];
-  interp.env.init (str, *this, fd);
-  get_seac_param_t  param;
-  param.init (this);
+  const hb_ubytes_t str = (*charStrings)[glyph];
+  cff1_cs_interp_env_t env (str, *this, fd);
+  cff1_cs_interpreter_t<cff1_cs_opset_seac_t, get_seac_param_t> interp (env);
+  get_seac_param_t  param (this);
   if (unlikely (!interp.interpret (param))) return false;
 
   if (param.has_seac ())
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh
index 542e3f4de35c04902267912c51aa9c2cc47cc692..4aa337f78be42593dca56e0bbb037528fe21f6d8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh
@@ -318,14 +318,21 @@ struct Charset0 {
     return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
   }
 
-  hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
   {
+    if (unlikely (glyph >= num_glyphs)) return 0;
     if (glyph == 0)
       return 0;
     else
       return sids[glyph - 1];
   }
 
+  void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+  {
+    for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++)
+      mapping->set (gid, sids[gid - 1]);
+  }
+
   hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
   {
     if (sid == 0)
@@ -381,20 +388,38 @@ struct Charset1_2 {
     return_trace (true);
   }
 
-  hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
   {
+    if (unlikely (glyph >= num_glyphs)) return 0;
     if (glyph == 0) return 0;
     glyph--;
     for (unsigned int i = 0;; i++)
     {
       if (glyph <= ranges[i].nLeft)
-	return (hb_codepoint_t)ranges[i].first + glyph;
+	return (hb_codepoint_t) ranges[i].first + glyph;
       glyph -= (ranges[i].nLeft + 1);
     }
 
     return 0;
   }
 
+  void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+  {
+    hb_codepoint_t gid = 1;
+    if (gid >= num_glyphs)
+      return;
+    for (unsigned i = 0;; i++)
+    {
+      hb_codepoint_t sid = ranges[i].first;
+      unsigned count = ranges[i].nLeft + 1;
+      for (unsigned j = 0; j < count; j++)
+	mapping->set (gid++, sid++);
+
+      if (gid >= num_glyphs)
+        break;
+    }
+  }
+
   hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
   {
     if (sid == 0) return 0;
@@ -521,16 +546,26 @@ struct Charset
 
   hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const
   {
-    if (unlikely (glyph >= num_glyphs)) return 0;
     switch (format)
     {
-    case 0: return u.format0.get_sid (glyph);
-    case 1: return u.format1.get_sid (glyph);
-    case 2: return u.format2.get_sid (glyph);
+    case 0: return u.format0.get_sid (glyph, num_glyphs);
+    case 1: return u.format1.get_sid (glyph, num_glyphs);
+    case 2: return u.format2.get_sid (glyph, num_glyphs);
     default:return 0;
     }
   }
 
+  void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+  {
+    switch (format)
+    {
+    case 0: u.format0.collect_glyph_to_sid_map (mapping, num_glyphs); return;
+    case 1: u.format1.collect_glyph_to_sid_map (mapping, num_glyphs); return;
+    case 2: u.format2.collect_glyph_to_sid_map (mapping, num_glyphs); return;
+    default:return;
+    }
+  }
+
   hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
   {
     switch (format)
@@ -602,6 +637,8 @@ struct cff1_top_dict_interp_env_t : num_interp_env_t
 {
   cff1_top_dict_interp_env_t ()
     : num_interp_env_t(), prev_offset(0), last_offset(0) {}
+  cff1_top_dict_interp_env_t (const hb_ubytes_t &bytes)
+    : num_interp_env_t(bytes), prev_offset(0), last_offset(0) {}
 
   unsigned int prev_offset;
   unsigned int last_offset;
@@ -1024,11 +1061,10 @@ struct cff1
       { fini (); return; }
 
       { /* parse top dict */
-	const byte_str_t topDictStr = (*topDictIndex)[0];
+	const hb_ubytes_t topDictStr = (*topDictIndex)[0];
 	if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
-	cff1_top_dict_interpreter_t top_interp;
-	top_interp.env.init (topDictStr);
-	topDict.init ();
+	cff1_top_dict_interp_env_t env (topDictStr);
+	cff1_top_dict_interpreter_t top_interp (env);
 	if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
       }
 
@@ -1098,20 +1134,21 @@ struct cff1
       {
 	for (unsigned int i = 0; i < fdCount; i++)
 	{
-	  byte_str_t fontDictStr = (*fdArray)[i];
+	  hb_ubytes_t fontDictStr = (*fdArray)[i];
 	  if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
 	  cff1_font_dict_values_t *font;
-	  cff1_font_dict_interpreter_t font_interp;
-	  font_interp.env.init (fontDictStr);
+	  cff1_top_dict_interp_env_t env (fontDictStr);
+	  cff1_font_dict_interpreter_t font_interp (env);
 	  font = fontDicts.push ();
-	  if (unlikely (font == &Crap (cff1_font_dict_values_t))) { fini (); return; }
+	  if (unlikely (fontDicts.in_error ())) { fini (); return; }
+
 	  font->init ();
 	  if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
 	  PRIVDICTVAL *priv = &privateDicts[i];
-	  const byte_str_t privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+	  const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
 	  if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
-	  dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp;
-	  priv_interp.env.init (privDictStr);
+	  num_interp_env_t env2 (privDictStr);
+	  dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2);
 	  priv->init ();
 	  if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
 
@@ -1126,10 +1163,10 @@ struct cff1
 	cff1_top_dict_values_t *font = &topDict;
 	PRIVDICTVAL *priv = &privateDicts[0];
 
-	const byte_str_t privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+	const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
 	if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
-	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp;
-	priv_interp.env.init (privDictStr);
+	num_interp_env_t env (privDictStr);
+	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env);
 	priv->init ();
 	if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
 
@@ -1194,6 +1231,19 @@ struct cff1
       }
     }
 
+    hb_map_t *create_glyph_to_sid_map () const
+    {
+      if (charset != &Null (Charset))
+      {
+	hb_map_t *mapping = hb_map_create ();
+	mapping->set (0, 0);
+	charset->collect_glyph_to_sid_map (mapping, num_glyphs);
+	return mapping;
+      }
+      else
+	return nullptr;
+    }
+
     hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const
     {
       if (charset != &Null (Charset))
@@ -1274,30 +1324,20 @@ struct cff1
     {
       SUPER::init (face);
 
+      glyph_names.set_relaxed (nullptr);
+
       if (!is_valid ()) return;
       if (is_CID ()) return;
 
-      /* fill glyph_names */
-      for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
-      {
-	hb_codepoint_t	sid = glyph_to_sid (gid);
-	gname_t	gname;
-	gname.sid = sid;
-	if (sid < cff1_std_strings_length)
-	  gname.name = cff1_std_strings (sid);
-	else
-	{
-	  byte_str_t	ustr = (*stringIndex)[sid - cff1_std_strings_length];
-	  gname.name = hb_bytes_t ((const char*)ustr.arrayZ, ustr.length);
-	}
-	if (unlikely (!gname.name.arrayZ)) { fini (); return; }
-	glyph_names.push (gname);
-      }
-      glyph_names.qsort ();
     }
     ~accelerator_t ()
     {
-      glyph_names.fini ();
+      hb_sorted_vector_t<gname_t> *names = glyph_names.get_relaxed ();
+      if (names)
+      {
+	names->fini ();
+	hb_free (names);
+      }
 
       SUPER::fini ();
     }
@@ -1305,9 +1345,9 @@ struct cff1
     bool get_glyph_name (hb_codepoint_t glyph,
 			 char *buf, unsigned int buf_len) const
     {
-      if (!buf) return true;
       if (unlikely (!is_valid ())) return false;
       if (is_CID()) return false;
+      if (unlikely (!buf_len)) return true;
       hb_codepoint_t sid = glyph_to_sid (glyph);
       const char *str;
       size_t str_len;
@@ -1319,7 +1359,7 @@ struct cff1
       }
       else
       {
-	byte_str_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length];
+	hb_ubytes_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length];
 	str = (const char *)ubyte_str.arrayZ;
 	str_len = ubyte_str.length;
       }
@@ -1333,11 +1373,53 @@ struct cff1
     bool get_glyph_from_name (const char *name, int len,
 			      hb_codepoint_t *glyph) const
     {
+      if (unlikely (!is_valid ())) return false;
+      if (is_CID()) return false;
       if (len < 0) len = strlen (name);
       if (unlikely (!len)) return false;
 
+    retry:
+      hb_sorted_vector_t<gname_t> *names = glyph_names.get ();
+      if (unlikely (!names))
+      {
+	names = (hb_sorted_vector_t<gname_t> *) hb_calloc (sizeof (hb_sorted_vector_t<gname_t>), 1);
+	if (likely (names))
+	{
+	  names->init ();
+	  /* TODO */
+
+	  /* fill glyph names */
+	  for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
+	  {
+	    hb_codepoint_t	sid = glyph_to_sid (gid);
+	    gname_t	gname;
+	    gname.sid = sid;
+	    if (sid < cff1_std_strings_length)
+	      gname.name = cff1_std_strings (sid);
+	    else
+	    {
+	      hb_ubytes_t	ustr = (*stringIndex)[sid - cff1_std_strings_length];
+	      gname.name = hb_bytes_t ((const char*) ustr.arrayZ, ustr.length);
+	    }
+	    if (unlikely (!gname.name.arrayZ))
+	      gname.name = hb_bytes_t ("", 0); /* To avoid nullptr. */
+	    names->push (gname);
+	  }
+	  names->qsort ();
+	}
+	if (unlikely (!glyph_names.cmpexch (nullptr, names)))
+	{
+	  if (names)
+	  {
+	    names->fini ();
+	    hb_free (names);
+	  }
+	  goto retry;
+	}
+      }
+
       gname_t key = { hb_bytes_t (name, len), 0 };
-      const gname_t *gname = glyph_names.bsearch (key);
+      const gname_t *gname = names ? names->bsearch (key) : nullptr;
       if (!gname) return false;
       hb_codepoint_t gid = sid_to_glyph (gname->sid);
       if (!gid && gname->sid) return false;
@@ -1359,7 +1441,7 @@ struct cff1
       {
 	const gname_t *a = (const gname_t *)a_;
 	const gname_t *b = (const gname_t *)b_;
-	int minlen = hb_min (a->name.length, b->name.length);
+	unsigned minlen = hb_min (a->name.length, b->name.length);
 	int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen);
 	if (ret) return ret;
 	return a->name.length - b->name.length;
@@ -1368,7 +1450,7 @@ struct cff1
       int cmp (const gname_t &a) const { return cmp (&a, this); }
     };
 
-    hb_sorted_vector_t<gname_t>	glyph_names;
+    mutable hb_atomic_ptr_t<hb_sorted_vector_t<gname_t>> glyph_names;
 
     typedef accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t> SUPER;
   };
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc
index 817fe064ce4591b99613292be707f0838be822f7..50c76daf93e76291ffc069418698b91274907f72 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc
@@ -36,9 +36,8 @@ using namespace CFF;
 
 struct cff2_extents_param_t
 {
-  void init ()
+  cff2_extents_param_t ()
   {
-    path_open = false;
     min_x.set_int (INT_MAX);
     min_y.set_int (INT_MAX);
     max_x.set_int (INT_MIN);
@@ -57,22 +56,22 @@ struct cff2_extents_param_t
     if (pt.y > max_y) max_y = pt.y;
   }
 
-  bool  path_open;
+  bool  path_open = false;
   number_t min_x;
   number_t min_y;
   number_t max_x;
   number_t max_y;
 };
 
-struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t, cff2_extents_param_t>
+struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t<number_t>, cff2_extents_param_t>
 {
-  static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt)
+  static void moveto (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt)
   {
     param.end_path ();
     env.moveto (pt);
   }
 
-  static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1)
+  static void line (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt1)
   {
     if (!param.is_path_open ())
     {
@@ -83,7 +82,7 @@ struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_
     param.update_bounds (env.get_pt ());
   }
 
-  static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+  static void curve (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
   {
     if (!param.is_path_open ())
     {
@@ -98,7 +97,7 @@ struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_
   }
 };
 
-struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_t, cff2_path_procs_extents_t> {};
+struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_t, number_t, cff2_path_procs_extents_t> {};
 
 bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
 					   hb_codepoint_t glyph,
@@ -112,11 +111,10 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
   if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
 
   unsigned int fd = fdSelect->get_fd (glyph);
-  cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t> interp;
-  const byte_str_t str = (*charStrings)[glyph];
-  interp.env.init (str, *this, fd, font->coords, font->num_coords);
+  const hb_ubytes_t str = (*charStrings)[glyph];
+  cff2_cs_interp_env_t<number_t> env (str, *this, fd, font->coords, font->num_coords);
+  cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t, number_t> interp (env);
   cff2_extents_param_t  param;
-  param.init ();
   if (unlikely (!interp.interpret (param))) return false;
 
   if (param.min_x >= param.max_x)
@@ -169,28 +167,28 @@ struct cff2_path_param_t
   hb_font_t *font;
 };
 
-struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_interp_env_t, cff2_path_param_t>
+struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_interp_env_t<number_t>, cff2_path_param_t>
 {
-  static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt)
+  static void moveto (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt)
   {
     param.move_to (pt);
     env.moveto (pt);
   }
 
-  static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1)
+  static void line (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt1)
   {
     param.line_to (pt1);
     env.moveto (pt1);
   }
 
-  static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+  static void curve (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
   {
     param.cubic_to (pt1, pt2, pt3);
     env.moveto (pt3);
   }
 };
 
-struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, cff2_path_procs_path_t> {};
+struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, number_t, cff2_path_procs_path_t> {};
 
 bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
 {
@@ -202,9 +200,9 @@ bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, h
   if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
 
   unsigned int fd = fdSelect->get_fd (glyph);
-  cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t> interp;
-  const byte_str_t str = (*charStrings)[glyph];
-  interp.env.init (str, *this, fd, font->coords, font->num_coords);
+  const hb_ubytes_t str = (*charStrings)[glyph];
+  cff2_cs_interp_env_t<number_t> env (str, *this, fd, font->coords, font->num_coords);
+  cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t, number_t> interp (env);
   cff2_path_param_t param (font, draw_session);
   if (unlikely (!interp.interpret (param))) return false;
   return true;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh
index b77e7f53fad8cc90ddd75c52423b4d330b11d498..746160dc8e430bc5259ecf3aa7565274c0cc1726 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh
@@ -247,12 +247,8 @@ typedef cff2_private_dict_values_base_t<num_dict_val_t> cff2_private_dict_values
 
 struct cff2_priv_dict_interp_env_t : num_interp_env_t
 {
-  void init (const byte_str_t &str)
-  {
-    num_interp_env_t::init (str);
-    ivs = 0;
-    seen_vsindex = false;
-  }
+  cff2_priv_dict_interp_env_t (const hb_ubytes_t &str) :
+    num_interp_env_t (str) {}
 
   void process_vsindex ()
   {
@@ -267,8 +263,8 @@ struct cff2_priv_dict_interp_env_t : num_interp_env_t
   void	 set_ivs (unsigned int ivs_) { ivs = ivs_; }
 
   protected:
-  unsigned int  ivs;
-  bool	  seen_vsindex;
+  unsigned int  ivs = 0;
+  bool	  seen_vsindex = false;
 };
 
 struct cff2_private_dict_opset_t : dict_opset_t
@@ -415,10 +411,10 @@ struct cff2
         goto fail;
 
       { /* parse top dict */
-	byte_str_t topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+	hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize);
 	if (unlikely (!topDictStr.sanitize (&sc))) goto fail;
-	cff2_top_dict_interpreter_t top_interp;
-	top_interp.env.init (topDictStr);
+	num_interp_env_t env (topDictStr);
+	cff2_top_dict_interpreter_t top_interp (env);
 	topDict.init ();
 	if (unlikely (!top_interp.interpret (topDict))) goto fail;
       }
@@ -447,20 +443,20 @@ struct cff2
       /* parse font dicts and gather private dicts */
       for (unsigned int i = 0; i < fdCount; i++)
       {
-	const byte_str_t fontDictStr = (*fdArray)[i];
+	const hb_ubytes_t fontDictStr = (*fdArray)[i];
 	if (unlikely (!fontDictStr.sanitize (&sc))) goto fail;
 	cff2_font_dict_values_t  *font;
-	cff2_font_dict_interpreter_t font_interp;
-	font_interp.env.init (fontDictStr);
+	num_interp_env_t env (fontDictStr);
+	cff2_font_dict_interpreter_t font_interp (env);
 	font = fontDicts.push ();
 	if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail;
 	font->init ();
 	if (unlikely (!font_interp.interpret (*font))) goto fail;
 
-	const byte_str_t privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
+	const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
 	if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
-	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t>  priv_interp;
-	priv_interp.env.init(privDictStr);
+	cff2_priv_dict_interp_env_t env2 (privDictStr);
+	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp (env2);
 	privateDicts[i].init ();
 	if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail;
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh
index a8747ee5a18664eb03cc39d68e3aa3cd4f480342..09c9fe93f32e57818db08accd1b19aeee473a928 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh
@@ -27,6 +27,8 @@
 #ifndef HB_OT_CMAP_TABLE_HH
 #define HB_OT_CMAP_TABLE_HH
 
+#include "hb-ot-os2-table.hh"
+#include "hb-ot-shaper-arabic-pua.hh"
 #include "hb-open-type.hh"
 #include "hb-set.hh"
 
@@ -44,7 +46,7 @@ struct CmapSubtableFormat0
   bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
-    if (!gid)
+    if (unlikely (!gid))
       return false;
     *glyph = gid;
     return true;
@@ -109,22 +111,26 @@ struct CmapSubtableFormat4
 
     while (it) {
       // Start a new range
-      start_cp = (*it).first;
-      prev_run_start_cp = (*it).first;
-      run_start_cp = (*it).first;
-      end_cp = (*it).first;
-      last_gid = (*it).second;
-      run_length = 1;
-      prev_delta = 0;
-
-      delta = (*it).second - (*it).first;
+      {
+        const auto& pair = *it;
+        start_cp = pair.first;
+        prev_run_start_cp = start_cp;
+        run_start_cp = start_cp;
+        end_cp = start_cp;
+        last_gid = pair.second;
+        run_length = 1;
+        prev_delta = 0;
+      }
+
+      delta = last_gid - start_cp;
       mode = FIRST_SUB_RANGE;
       it++;
 
       while (it) {
         // Process range
-        hb_codepoint_t next_cp = (*it).first;
-        hb_codepoint_t next_gid = (*it).second;
+        const auto& pair = *it;
+        hb_codepoint_t next_cp = pair.first;
+        hb_codepoint_t next_gid = pair.second;
         if (next_cp != end_cp + 1) {
           // Current range is over, stop processing.
           break;
@@ -282,23 +288,22 @@ struct CmapSubtableFormat4
   }
 
   template<typename Iterator,
-	   hb_requires (hb_is_iterator (Iterator))>
+          hb_requires (hb_is_iterator (Iterator))>
   HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c,
-					 Iterator it,
+                                         Iterator it,
 					 HBUINT16 *endCode,
 					 HBUINT16 *startCode,
 					 HBINT16 *idDelta,
 					 unsigned segcount)
   {
-    hb_hashmap_t<hb_codepoint_t, hb_codepoint_t> cp_to_gid;
-    + it | hb_sink (cp_to_gid);
+    hb_map_t cp_to_gid { it };
 
     HBUINT16 *idRangeOffset = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
     if (unlikely (!c->check_success (idRangeOffset))) return nullptr;
     if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr;
 
     for (unsigned i : + hb_range (segcount)
-             | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }))
+		      | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }))
     {
       idRangeOffset[i] = 2 * (c->start_embed<HBUINT16> () - idRangeOffset - i);
       for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++)
@@ -323,22 +328,31 @@ struct CmapSubtableFormat4
 		 { return _.first <= 0xFFFF; })
     ;
 
-    if (format4_iter.len () == 0) return;
+    if (!format4_iter) return;
 
     unsigned table_initpos = c->length ();
     if (unlikely (!c->extend_min (this))) return;
     this->format = 4;
 
+    hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> cp_to_gid {
+      format4_iter
+    };
+
     //serialize endCode[], startCode[], idDelta[]
     HBUINT16* endCode = c->start_embed<HBUINT16> ();
-    unsigned segcount = serialize_find_segcount (format4_iter);
-    if (unlikely (!serialize_start_end_delta_arrays (c, format4_iter, segcount)))
+    unsigned segcount = serialize_find_segcount (cp_to_gid.iter());
+    if (unlikely (!serialize_start_end_delta_arrays (c, cp_to_gid.iter(), segcount)))
       return;
 
     HBUINT16 *startCode = endCode + segcount + 1;
     HBINT16 *idDelta = ((HBINT16*)startCode) + segcount;
 
-    HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount);
+    HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c,
+                                                           cp_to_gid.iter (),
+                                                           endCode,
+                                                           startCode,
+                                                           idDelta,
+                                                           segcount);
     if (unlikely (!c->check_success (idRangeOffset))) return;
 
     this->length = c->length () - table_initpos;
@@ -401,7 +415,7 @@ struct CmapSubtableFormat4
 					  2,
 					  _hb_cmp_method<hb_codepoint_t, CustomRange, unsigned>,
 					  this->segCount + 1);
-      if (!found)
+      if (unlikely (!found))
 	return false;
       unsigned int i = found - endCount;
 
@@ -421,7 +435,7 @@ struct CmapSubtableFormat4
 	gid += this->idDelta[i];
       }
       gid &= 0xFFFFu;
-      if (!gid)
+      if (unlikely (!gid))
 	return false;
       *glyph = gid;
       return true;
@@ -440,14 +454,14 @@ struct CmapSubtableFormat4
 	hb_codepoint_t start = this->startCount[i];
 	hb_codepoint_t end = this->endCount[i];
 	unsigned int rangeOffset = this->idRangeOffset[i];
+        out->add_range(start, end);
 	if (rangeOffset == 0)
 	{
 	  for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
 	  {
 	    hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu;
 	    if (unlikely (!gid))
-	      continue;
-	    out->add (codepoint);
+              out->del(codepoint);
 	  }
 	}
 	else
@@ -456,11 +470,13 @@ struct CmapSubtableFormat4
 	  {
 	    unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
 	    if (unlikely (index >= this->glyphIdArrayLength))
+            {
+              out->del_range (codepoint, end);
 	      break;
+            }
 	    hb_codepoint_t gid = this->glyphIdArray[index];
 	    if (unlikely (!gid))
-	      continue;
-	    out->add (codepoint);
+              out->del(codepoint);
 	  }
 	}
       }
@@ -469,6 +485,8 @@ struct CmapSubtableFormat4
     void collect_mapping (hb_set_t *unicodes, /* OUT */
 			  hb_map_t *mapping /* OUT */) const
     {
+      // TODO(grieger): optimize similar to collect_unicodes
+      // (ie. use add_range())
       unsigned count = this->segCount;
       if (count && this->startCount[count - 1] == 0xFFFFu)
 	count--; /* Skip sentinel segment. */
@@ -620,7 +638,7 @@ struct CmapSubtableTrimmed
   {
     /* Rely on our implicit array bound-checking. */
     hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
-    if (!gid)
+    if (unlikely (!gid))
       return false;
     *glyph = gid;
     return true;
@@ -674,7 +692,7 @@ struct CmapSubtableTrimmed
 };
 
 struct CmapSubtableFormat6  : CmapSubtableTrimmed<HBUINT16> {};
-struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};
+struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32> {};
 
 template <typename T>
 struct CmapSubtableLongSegmented
@@ -684,7 +702,7 @@ struct CmapSubtableLongSegmented
   bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint);
-    if (!gid)
+    if (unlikely (!gid))
       return false;
     *glyph = gid;
     return true;
@@ -722,11 +740,19 @@ struct CmapSubtableLongSegmented
 			hb_map_t *mapping, /* OUT */
 			unsigned num_glyphs) const
   {
+    hb_codepoint_t last_end = 0;
     for (unsigned i = 0; i < this->groups.len; i++)
     {
       hb_codepoint_t start = this->groups[i].startCharCode;
       hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode,
 				   (hb_codepoint_t) HB_UNICODE_MAX);
+      if (unlikely (start > end || start < last_end)) {
+        // Range is not in order and is invalid, skip it.
+        continue;
+      }
+      last_end = end;
+
+
       hb_codepoint_t gid = this->groups[i].glyphID;
       if (!gid)
       {
@@ -778,16 +804,16 @@ struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
   void serialize (hb_serialize_context_t *c,
 		  Iterator it)
   {
-    if (it.len () == 0) return;
+    if (!it) return;
     unsigned table_initpos = c->length ();
     if (unlikely (!c->extend_min (this))) return;
 
-    hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF;
+    hb_codepoint_t startCharCode = (hb_codepoint_t) -1, endCharCode = (hb_codepoint_t) -1;
     hb_codepoint_t glyphID = 0;
 
     for (const auto& _ : +it)
     {
-      if (startCharCode == 0xFFFF)
+      if (startCharCode == (hb_codepoint_t) -1)
       {
 	startCharCode = _.first;
 	endCharCode = _.first;
@@ -818,7 +844,7 @@ struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
     this->format = 12;
     this->reserved = 0;
     this->length = c->length () - table_initpos;
-    this->groups.len = (this->length - min_size)/CmapSubtableLongGroup::static_size;
+    this->groups.len = (this->length - min_size) / CmapSubtableLongGroup::static_size;
   }
 
   static size_t get_sub_table_size (const hb_sorted_vector_t<CmapSubtableLongGroup> &groups_data)
@@ -1448,6 +1474,51 @@ struct EncodingRecord
   DEFINE_SIZE_STATIC (8);
 };
 
+struct SubtableUnicodesCache {
+
+ private:
+  const void* base;
+  hb_hashmap_t<intptr_t, hb::unique_ptr<hb_set_t>> cached_unicodes;
+
+ public:
+  SubtableUnicodesCache(const void* cmap_base)
+      : base(cmap_base), cached_unicodes() {}
+
+  hb_set_t* set_for (const EncodingRecord* record)
+  {
+    if (!cached_unicodes.has ((intptr_t) record))
+    {
+      hb_set_t *s = hb_set_create ();
+      if (unlikely (s->in_error ()))
+	return hb_set_get_empty ();
+	
+      (base+record->subtable).collect_unicodes (s);
+
+      if (unlikely (!cached_unicodes.set ((intptr_t) record, hb::unique_ptr<hb_set_t> {s})))
+        return hb_set_get_empty ();
+
+      return s;
+    }
+    return cached_unicodes.get ((intptr_t) record);
+  }
+
+};
+
+static inline uint_fast16_t
+_hb_symbol_pua_map (unsigned codepoint)
+{
+  if (codepoint <= 0x00FFu)
+  {
+    /* For symbol-encoded OpenType fonts, we duplicate the
+     * U+F000..F0FF range at U+0000..U+00FF.  That's what
+     * Windows seems to do, and that's hinted about at:
+     * https://docs.microsoft.com/en-us/typography/opentype/spec/recom
+     * under "Non-Standard (Symbol) Fonts". */
+    return 0xF000u + codepoint;
+  }
+  return 0;
+}
+
 struct cmap
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap;
@@ -1467,6 +1538,7 @@ struct cmap
     unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
     auto snap = c->snapshot ();
 
+    SubtableUnicodesCache unicodes_cache (base);
     for (const EncodingRecord& _ : encodingrec_iter)
     {
       if (c->in_error ())
@@ -1475,12 +1547,11 @@ struct cmap
       unsigned format = (base+_.subtable).u.format;
       if (format != 4 && format != 12 && format != 14) continue;
 
-      hb_set_t unicodes_set;
-      (base+_.subtable).collect_unicodes (&unicodes_set);
+      hb_set_t* unicodes_set = unicodes_cache.set_for (&_);
 
       if (!drop_format_4 && format == 4)
       {
-        c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx);
+        c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 4u, base, plan, &format4objidx);
         if (c->in_error () && c->only_overflow ())
         {
           // cmap4 overflowed, reset and retry serialization without format 4 subtables.
@@ -1495,8 +1566,8 @@ struct cmap
 
       else if (format == 12)
       {
-        if (_can_drop (_, unicodes_set, base, + it | hb_map (hb_first), encodingrec_iter)) continue;
-        c->copy (_, + it | hb_filter (unicodes_set, hb_first), 12u, base, plan, &format12objidx);
+        if (_can_drop (_, *unicodes_set, base, unicodes_cache, + it | hb_map (hb_first), encodingrec_iter)) continue;
+        c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx);
       }
       else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx);
     }
@@ -1514,6 +1585,7 @@ struct cmap
   bool _can_drop (const EncodingRecord& cmap12,
                   const hb_set_t& cmap12_unicodes,
                   const void* base,
+                  SubtableUnicodesCache& unicodes_cache,
                   Iterator subset_unicodes,
                   EncodingRecordIterator encoding_records)
   {
@@ -1544,11 +1616,10 @@ struct cmap
           || (base+_.subtable).get_language() != target_language)
         continue;
 
-      hb_set_t sibling_unicodes;
-      (base+_.subtable).collect_unicodes (&sibling_unicodes);
+      hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_);
 
       auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes);
-      auto sibling = + subset_unicodes | hb_filter (sibling_unicodes);
+      auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes);
       for (; cmap12 && sibling; cmap12++, sibling++)
       {
         unsigned a = *cmap12;
@@ -1616,13 +1687,7 @@ struct cmap
     if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false);
 
     auto it =
-    + hb_iter (c->plan->unicodes)
-    | hb_map ([&] (hb_codepoint_t _)
-	      {
-		hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID;
-		c->plan->new_gid_for_codepoint (_, &new_gid);
-		return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, new_gid);
-	      })
+    + c->plan->unicode_to_new_gid_list.iter ()
     | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
 		 { return (_.second != HB_MAP_VALUE_INVALID); })
     ;
@@ -1677,7 +1742,24 @@ struct cmap
 
       this->get_glyph_data = subtable;
       if (unlikely (symbol))
-	this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable>;
+      {
+	switch ((unsigned) face->table.OS2->get_font_page ()) {
+	case OS2::font_page_t::FONT_PAGE_NONE:
+	  this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_symbol_pua_map>;
+	  break;
+#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK
+	case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC:
+	  this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_simp_map>;
+	  break;
+	case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC:
+	  this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_trad_map>;
+	  break;
+#endif
+	default:
+	  this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
+	  break;
+	}
+      }
       else
       {
 	switch (subtable->u.format) {
@@ -1759,6 +1841,7 @@ struct cmap
     typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
 					      hb_codepoint_t codepoint,
 					      hb_codepoint_t *glyph);
+    typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned);
 
     template <typename Type>
     HB_INTERNAL static bool get_glyph_from (const void *obj,
@@ -1769,7 +1852,7 @@ struct cmap
       return typed_obj->get_glyph (codepoint, glyph);
     }
 
-    template <typename Type>
+    template <typename Type, hb_pua_remap_func_t remap>
     HB_INTERNAL static bool get_glyph_from_symbol (const void *obj,
 						   hb_codepoint_t codepoint,
 						   hb_codepoint_t *glyph)
@@ -1778,15 +1861,8 @@ struct cmap
       if (likely (typed_obj->get_glyph (codepoint, glyph)))
 	return true;
 
-      if (codepoint <= 0x00FFu)
-      {
-	/* For symbol-encoded OpenType fonts, we duplicate the
-	 * U+F000..F0FF range at U+0000..U+00FF.  That's what
-	 * Windows seems to do, and that's hinted about at:
-	 * https://docs.microsoft.com/en-us/typography/opentype/spec/recom
-	 * under "Non-Standard (Symbol) Fonts". */
-	return typed_obj->get_glyph (0xF000u + codepoint, glyph);
-      }
+      if (hb_codepoint_t c = remap (codepoint))
+	return typed_obj->get_glyph (c, glyph);
 
       return false;
     }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh
index a9deeba9a7a835910c879cb25c5ee086cf99d4ea..bcab77f79dc7cdecb36e071b60f6363e2eafe6c8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh
@@ -97,9 +97,10 @@ struct CPALV1Tail
       c->push ();
       for (const auto _ : colorLabels)
       {
-        if (!color_index_map->has (_)) continue;
+	const hb_codepoint_t *v;
+        if (!color_index_map->has (_, &v)) continue;
         NameID new_color_idx;
-        new_color_idx = color_index_map->get (_);
+	new_color_idx = *v;
         if (!c->copy<NameID> (new_color_idx))
         {
           c->pop_discard ();
@@ -197,30 +198,38 @@ struct CPAL
 
   public:
   bool serialize (hb_serialize_context_t *c,
-                  const hb_array_t<const BGRAColor> &color_records,
                   const hb_array_t<const HBUINT16> &color_record_indices,
-                  const hb_map_t &color_record_index_map,
-                  const hb_set_t &retained_color_record_indices) const
+                  const hb_array_t<const BGRAColor> &color_records,
+                  const hb_vector_t<unsigned>& first_color_index_for_layer,
+                  const hb_map_t& first_color_to_layer_index,
+                  const hb_set_t &retained_color_indices) const
   {
     TRACE_SERIALIZE (this);
 
+    // TODO(grieger): limit total final size.
+
     for (const auto idx : color_record_indices)
     {
+      hb_codepoint_t layer_index = first_color_to_layer_index[idx];
+
       HBUINT16 new_idx;
-      if (idx == 0) new_idx = 0;
-      else new_idx = color_record_index_map.get (idx);
+      new_idx = layer_index * retained_color_indices.get_population ();
       if (!c->copy<HBUINT16> (new_idx)) return_trace (false);
     }
 
     c->push ();
-    for (const auto _ : retained_color_record_indices.iter ())
+    for (unsigned first_color_index : first_color_index_for_layer)
     {
-      if (!c->copy<BGRAColor> (color_records[_]))
+      for (hb_codepoint_t color_index : retained_color_indices)
       {
-        c->pop_discard ();
-        return_trace (false);
+        if (!c->copy<BGRAColor> (color_records[first_color_index + color_index]))
+        {
+          c->pop_discard ();
+          return_trace (false);
+        }
       }
     }
+
     c->add_link (colorRecordsZ, c->pop_pack ());
     return_trace (true);
   }
@@ -228,6 +237,8 @@ struct CPAL
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
+    if (!numPalettes) return_trace (false);
+
     const hb_map_t *color_index_map = c->plan->colr_palettes;
     if (color_index_map->is_empty ()) return_trace (false);
 
@@ -242,30 +253,34 @@ struct CPAL
     auto *out = c->serializer->start_embed (*this);
     if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
 
+
     out->version = version;
     out->numColors = retained_color_indices.get_population ();
     out->numPalettes = numPalettes;
 
-    const hb_array_t<const HBUINT16> colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes);
-    hb_map_t color_record_index_map;
-    hb_set_t retained_color_record_indices;
+    hb_vector_t<unsigned> first_color_index_for_layer;
+    hb_map_t first_color_to_layer_index;
 
-    unsigned record_count = 0;
+    const hb_array_t<const HBUINT16> colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes);
     for (const auto first_color_record_idx : colorRecordIndices)
     {
-      for (unsigned retained_color_idx : retained_color_indices.iter ())
-      {
-        unsigned color_record_idx = first_color_record_idx + retained_color_idx;
-        if (color_record_index_map.has (color_record_idx)) continue;
-        color_record_index_map.set (color_record_idx, record_count);
-        retained_color_record_indices.add (color_record_idx);
-        record_count++;
-      }
+      if (first_color_to_layer_index.has (first_color_record_idx)) continue;
+
+      first_color_index_for_layer.push (first_color_record_idx);
+      first_color_to_layer_index.set (first_color_record_idx,
+                                      first_color_index_for_layer.length - 1);
     }
 
-    out->numColorRecords = record_count;
+    out->numColorRecords = first_color_index_for_layer.length
+                           * retained_color_indices.get_population ();
+
     const hb_array_t<const BGRAColor> color_records = (this+colorRecordsZ).as_array (numColorRecords);
-    if (!out->serialize (c->serializer, color_records, colorRecordIndices, color_record_index_map, retained_color_record_indices))
+    if (!out->serialize (c->serializer,
+                         colorRecordIndices,
+                         color_records,
+                         first_color_index_for_layer,
+                         first_color_to_layer_index,
+                         retained_color_indices))
       return_trace (false);
 
     if (version == 1)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-sbix-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-sbix-table.hh
index 9741ebd4503372a3dcf3d924797b78ec710f2d85..d0e2235fb2f0125a8dabeeb7439cf8b04ce3427e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-sbix-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-sbix-table.hh
@@ -298,6 +298,12 @@ struct sbix
 
       const PNGHeader &png = *blob->as<PNGHeader>();
 
+      if (png.IHDR.height >= 65536 || png.IHDR.width >= 65536)
+      {
+	hb_blob_destroy (blob);
+	return false;
+      }
+
       extents->x_bearing = x_offset;
       extents->y_bearing = png.IHDR.height + y_offset;
       extents->width     = png.IHDR.width;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-font.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-font.cc
index 0f44ee4d5f4f1b14d84f0169f86fa17346147624..af1bc86d4886069aae2a6ebeec2063301e139a5d 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-font.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-font.cc
@@ -30,6 +30,7 @@
 
 #include "hb-ot.h"
 
+#include "hb-cache.hh"
 #include "hb-font.hh"
 #include "hb-machinery.hh"
 #include "hb-ot-face.hh"
@@ -58,6 +59,41 @@
  * never need to call these functions directly.
  **/
 
+struct hb_ot_font_t
+{
+  const hb_ot_face_t *ot_face;
+
+  /* h_advance caching */
+  mutable hb_atomic_int_t cached_coords_serial;
+  mutable hb_atomic_ptr_t<hb_advance_cache_t> advance_cache;
+};
+
+static hb_ot_font_t *
+_hb_ot_font_create (hb_font_t *font)
+{
+  hb_ot_font_t *ot_font = (hb_ot_font_t *) hb_calloc (1, sizeof (hb_ot_font_t));
+  if (unlikely (!ot_font))
+    return nullptr;
+
+  ot_font->ot_face = &font->face->table;
+
+  return ot_font;
+}
+
+static void
+_hb_ot_font_destroy (void *font_data)
+{
+  hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data;
+
+  auto *cache = ot_font->advance_cache.get_relaxed ();
+  if (cache)
+  {
+    cache->fini ();
+    hb_free (cache);
+  }
+
+  hb_free (ot_font);
+}
 
 static hb_bool_t
 hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED,
@@ -66,7 +102,8 @@ hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED,
 			 hb_codepoint_t *glyph,
 			 void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
   return ot_face->cmap->get_nominal_glyph (unicode, glyph);
 }
 
@@ -80,7 +117,8 @@ hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
 			  unsigned int glyph_stride,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
   return ot_face->cmap->get_nominal_glyphs (count,
 					    first_unicode, unicode_stride,
 					    first_glyph, glyph_stride);
@@ -94,7 +132,8 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED,
 			   hb_codepoint_t *glyph,
 			   void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
   return ot_face->cmap->get_variation_glyph (unicode, variation_selector, glyph);
 }
 
@@ -107,15 +146,83 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
   const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
 
-  for (unsigned int i = 0; i < count; i++)
+#ifndef HB_NO_VAR
+  const OT::HVARVVAR &HVAR = *hmtx.var_table;
+  const OT::VariationStore &varStore = &HVAR + HVAR.varStore;
+  OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr;
+
+  bool use_cache = font->num_coords;
+#else
+  OT::VariationStore::cache_t *varStore_cache = nullptr;
+  bool use_cache = false;
+#endif
+
+  hb_advance_cache_t *cache = nullptr;
+  if (use_cache)
   {
-    *first_advance = font->em_scale_x (hmtx.get_advance (*first_glyph, font));
-    first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
-    first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+  retry:
+    cache = ot_font->advance_cache.get ();
+    if (unlikely (!cache))
+    {
+      cache = (hb_advance_cache_t *) hb_malloc (sizeof (hb_advance_cache_t));
+      if (unlikely (!cache))
+      {
+	use_cache = false;
+	goto out;
+      }
+
+      cache->init ();
+      if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache)))
+      {
+	hb_free (cache);
+	goto retry;
+      }
+      ot_font->cached_coords_serial.set (font->serial_coords);
+    }
+  }
+  out:
+
+  if (!use_cache)
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->em_scale_x (hmtx.get_advance (*first_glyph, font, varStore_cache));
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
   }
+  else
+  { /* Use cache. */
+    if (ot_font->cached_coords_serial.get () != (int) font->serial_coords)
+    {
+      ot_font->advance_cache->init ();
+      ot_font->cached_coords_serial.set (font->serial_coords);
+    }
+
+    for (unsigned int i = 0; i < count; i++)
+    {
+      hb_position_t v;
+      unsigned cv;
+      if (ot_font->advance_cache->get (*first_glyph, &cv))
+	v = cv;
+      else
+      {
+        v = hmtx.get_advance (*first_glyph, font, varStore_cache);
+	ot_font->advance_cache->set (*first_glyph, v);
+      }
+      *first_advance = font->em_scale_x (v);
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+  }
+
+#ifndef HB_NO_VAR
+  OT::VariationStore::destroy_cache (varStore_cache);
+#endif
 }
 
 #ifndef HB_NO_VERTICAL
@@ -128,16 +235,31 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
   const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
 
   if (vmtx.has_data ())
+  {
+#ifndef HB_NO_VAR
+    const OT::HVARVVAR &VVAR = *vmtx.var_table;
+    const OT::VariationStore &varStore = &VVAR + VVAR.varStore;
+    OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr;
+#else
+    OT::VariationStore::cache_t *varStore_cache = nullptr;
+#endif
+
     for (unsigned int i = 0; i < count; i++)
     {
-      *first_advance = font->em_scale_y (-(int) vmtx.get_advance (*first_glyph, font));
+      *first_advance = font->em_scale_y (-(int) vmtx.get_advance (*first_glyph, font, varStore_cache));
       first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
+
+#ifndef HB_NO_VAR
+    OT::VariationStore::destroy_cache (varStore_cache);
+#endif
+  }
   else
   {
     hb_font_extents_t font_extents;
@@ -163,7 +285,8 @@ hb_ot_get_glyph_v_origin (hb_font_t *font,
 			  hb_position_t *y,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
 
   *x = font->get_glyph_h_advance (glyph) / 2;
 
@@ -208,7 +331,8 @@ hb_ot_get_glyph_extents (hb_font_t *font,
 			 hb_glyph_extents_t *extents,
 			 void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
 
 #if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
   if (ot_face->sbix->get_extents (font, glyph, extents)) return true;
@@ -234,7 +358,9 @@ hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED,
 		      char *name, unsigned int size,
 		      void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
+
   if (ot_face->post->get_glyph_name (glyph, name, size)) return true;
 #ifndef HB_NO_OT_FONT_CFF
   if (ot_face->cff1->get_glyph_name (glyph, name, size)) return true;
@@ -248,7 +374,9 @@ hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED,
 			   hb_codepoint_t *glyph,
 			   void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
+  const hb_ot_face_t *ot_face = ot_font->ot_face;
+
   if (ot_face->post->get_glyph_from_name (name, len, glyph)) return true;
 #ifndef HB_NO_OT_FONT_CFF
     if (ot_face->cff1->get_glyph_from_name (name, len, glyph)) return true;
@@ -364,10 +492,14 @@ _hb_ot_get_font_funcs ()
 void
 hb_ot_font_set_funcs (hb_font_t *font)
 {
+  hb_ot_font_t *ot_font = _hb_ot_font_create (font);
+  if (unlikely (!ot_font))
+    return;
+
   hb_font_set_funcs (font,
 		     _hb_ot_get_font_funcs (),
-		     &font->face->table,
-		     nullptr);
+		     ot_font,
+		     _hb_ot_font_destroy);
 }
 
 #ifndef HB_NO_VAR
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh
index b4ac688344395a12ce8c3e147af06ceb26ddf187..c32ff7636d5def9772962f7e4a764038b174f061 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh
@@ -30,1332 +30,6 @@
 #ifndef HB_OT_GLYF_TABLE_HH
 #define HB_OT_GLYF_TABLE_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-head-table.hh"
-#include "hb-ot-hmtx-table.hh"
-#include "hb-ot-var-gvar-table.hh"
-#include "hb-draw.hh"
-
-namespace OT {
-
-
-/*
- * loca -- Index to Location
- * https://docs.microsoft.com/en-us/typography/opentype/spec/loca
- */
-#define HB_OT_TAG_loca HB_TAG('l','o','c','a')
-
-#ifndef HB_MAX_COMPOSITE_OPERATIONS
-#define HB_MAX_COMPOSITE_OPERATIONS 100000
-#endif
-
-
-struct loca
-{
-  friend struct glyf;
-
-  static constexpr hb_tag_t tableTag = HB_OT_TAG_loca;
-
-  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (true);
-  }
-
-  protected:
-  UnsizedArrayOf<HBUINT8>
-		dataZ;	/* Location data. */
-  public:
-  DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
-			 * check the size externally, allow Null() object of it by
-			 * defining it _MIN instead. */
-};
-
-
-/*
- * glyf -- TrueType Glyph Data
- * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
- */
-#define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
-
-
-struct glyf
-{
-  static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
-
-  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
-  {
-    TRACE_SANITIZE (this);
-    /* Runtime checks as eager sanitizing each glyph is costy */
-    return_trace (true);
-  }
-
-  template<typename Iterator,
-	   hb_requires (hb_is_source_of (Iterator, unsigned int))>
-  static bool
-  _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca)
-  {
-    unsigned num_offsets = padded_offsets.len () + 1;
-    unsigned entry_size = use_short_loca ? 2 : 4;
-    char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets);
-
-    if (unlikely (!loca_prime_data)) return false;
-
-    DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d",
-	       entry_size, num_offsets, entry_size * num_offsets);
-
-    if (use_short_loca)
-      _write_loca (padded_offsets, 1, hb_array ((HBUINT16 *) loca_prime_data, num_offsets));
-    else
-      _write_loca (padded_offsets, 0, hb_array ((HBUINT32 *) loca_prime_data, num_offsets));
-
-    hb_blob_t *loca_blob = hb_blob_create (loca_prime_data,
-					   entry_size * num_offsets,
-					   HB_MEMORY_MODE_WRITABLE,
-					   loca_prime_data,
-					   hb_free);
-
-    bool result = plan->add_table (HB_OT_TAG_loca, loca_blob)
-	       && _add_head_and_set_loca_version (plan, use_short_loca);
-
-    hb_blob_destroy (loca_blob);
-    return result;
-  }
-
-  template<typename IteratorIn, typename IteratorOut,
-	   hb_requires (hb_is_source_of (IteratorIn, unsigned int)),
-	   hb_requires (hb_is_sink_of (IteratorOut, unsigned))>
-  static void
-  _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest)
-  {
-    unsigned int offset = 0;
-    dest << 0;
-    + it
-    | hb_map ([=, &offset] (unsigned int padded_size)
-	      {
-		offset += padded_size;
-		DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset);
-		return offset >> right_shift;
-	      })
-    | hb_sink (dest)
-    ;
-  }
-
-  /* requires source of SubsetGlyph complains the identifier isn't declared */
-  template <typename Iterator>
-  bool serialize (hb_serialize_context_t *c,
-		  Iterator it,
-                  bool use_short_loca,
-		  const hb_subset_plan_t *plan)
-  {
-    TRACE_SERIALIZE (this);
-    unsigned init_len = c->length ();
-    for (const auto &_ : it) _.serialize (c, use_short_loca, plan);
-
-    /* As a special case when all glyph in the font are empty, add a zero byte
-     * to the table, so that OTS doesn’t reject it, and to make the table work
-     * on Windows as well.
-     * See https://github.com/khaledhosny/ots/issues/52 */
-    if (init_len == c->length ())
-    {
-      HBUINT8 empty_byte;
-      empty_byte = 0;
-      c->copy (empty_byte);
-    }
-    return_trace (true);
-  }
-
-  /* Byte region(s) per glyph to output
-     unpadded, hints removed if so requested
-     If we fail to process a glyph we produce an empty (0-length) glyph */
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-
-    glyf *glyf_prime = c->serializer->start_embed <glyf> ();
-    if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
-
-    hb_vector_t<SubsetGlyph> glyphs;
-    _populate_subset_glyphs (c->plan, &glyphs);
-
-    auto padded_offsets =
-    + hb_iter (glyphs)
-    | hb_map (&SubsetGlyph::padded_size)
-    ;
-
-    unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0);
-    bool use_short_loca = max_offset < 0x1FFFF;
-
-
-    glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
-    if (!use_short_loca) {
-      padded_offsets =
-          + hb_iter (glyphs)
-          | hb_map (&SubsetGlyph::length)
-          ;
-    }
-
-
-    if (unlikely (c->serializer->in_error ())) return_trace (false);
-    return_trace (c->serializer->check_success (_add_loca_and_head (c->plan,
-								    padded_offsets,
-                                                                    use_short_loca)));
-  }
-
-  template <typename SubsetGlyph>
-  void
-  _populate_subset_glyphs (const hb_subset_plan_t   *plan,
-			   hb_vector_t<SubsetGlyph> *glyphs /* OUT */) const
-  {
-    OT::glyf::accelerator_t glyf (plan->source);
-
-    + hb_range (plan->num_output_glyphs ())
-    | hb_map ([&] (hb_codepoint_t new_gid)
-	      {
-		SubsetGlyph subset_glyph = {0};
-		subset_glyph.new_gid = new_gid;
-
-		/* should never fail: all old gids should be mapped */
-		if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid))
-		  return subset_glyph;
-
-		if (new_gid == 0 &&
-                    !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
-		  subset_glyph.source_glyph = Glyph ();
-		else
-		  subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
-		if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
-                  subset_glyph.drop_hints_bytes ();
-		else
-                  subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
-		return subset_glyph;
-	      })
-    | hb_sink (glyphs)
-    ;
-  }
-
-  static bool
-  _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
-  {
-    hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<head> (plan->source);
-    hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
-    hb_blob_destroy (head_blob);
-
-    if (unlikely (!head_prime_blob))
-      return false;
-
-    head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
-    head_prime->indexToLocFormat = use_short_loca ? 0 : 1;
-    bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
-
-    hb_blob_destroy (head_prime_blob);
-    return success;
-  }
-
-  struct CompositeGlyphChain
-  {
-    protected:
-    enum composite_glyph_flag_t
-    {
-      ARG_1_AND_2_ARE_WORDS	= 0x0001,
-      ARGS_ARE_XY_VALUES	= 0x0002,
-      ROUND_XY_TO_GRID		= 0x0004,
-      WE_HAVE_A_SCALE		= 0x0008,
-      MORE_COMPONENTS		= 0x0020,
-      WE_HAVE_AN_X_AND_Y_SCALE	= 0x0040,
-      WE_HAVE_A_TWO_BY_TWO	= 0x0080,
-      WE_HAVE_INSTRUCTIONS	= 0x0100,
-      USE_MY_METRICS		= 0x0200,
-      OVERLAP_COMPOUND		= 0x0400,
-      SCALED_COMPONENT_OFFSET	= 0x0800,
-      UNSCALED_COMPONENT_OFFSET = 0x1000
-    };
-
-    public:
-    unsigned int get_size () const
-    {
-      unsigned int size = min_size;
-      /* arg1 and 2 are int16 */
-      if (flags & ARG_1_AND_2_ARE_WORDS) size += 4;
-      /* arg1 and 2 are int8 */
-      else size += 2;
-
-      /* One x 16 bit (scale) */
-      if (flags & WE_HAVE_A_SCALE) size += 2;
-      /* Two x 16 bit (xscale, yscale) */
-      else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4;
-      /* Four x 16 bit (xscale, scale01, scale10, yscale) */
-      else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8;
-
-      return size;
-    }
-
-    void set_glyph_index (hb_codepoint_t new_gid) { glyphIndex = new_gid; }
-    hb_codepoint_t get_glyph_index ()       const { return glyphIndex; }
-
-    void drop_instructions_flag ()  { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; }
-    void set_overlaps_flag ()
-    {
-      flags = (uint16_t) flags | OVERLAP_COMPOUND;
-    }
-
-    bool has_instructions ()  const { return   flags & WE_HAVE_INSTRUCTIONS; }
-
-    bool has_more ()          const { return   flags & MORE_COMPONENTS; }
-    bool is_use_my_metrics () const { return   flags & USE_MY_METRICS; }
-    bool is_anchored ()       const { return !(flags & ARGS_ARE_XY_VALUES); }
-    void get_anchor_points (unsigned int &point1, unsigned int &point2) const
-    {
-      const HBUINT8 *p = &StructAfter<const HBUINT8> (glyphIndex);
-      if (flags & ARG_1_AND_2_ARE_WORDS)
-      {
-	point1 = ((const HBUINT16 *) p)[0];
-	point2 = ((const HBUINT16 *) p)[1];
-      }
-      else
-      {
-	point1 = p[0];
-	point2 = p[1];
-      }
-    }
-
-    void transform_points (contour_point_vector_t &points) const
-    {
-      float matrix[4];
-      contour_point_t trans;
-      if (get_transformation (matrix, trans))
-      {
-	if (scaled_offsets ())
-	{
-	  points.translate (trans);
-	  points.transform (matrix);
-	}
-	else
-	{
-	  points.transform (matrix);
-	  points.translate (trans);
-	}
-      }
-    }
-
-    protected:
-    bool scaled_offsets () const
-    { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; }
-
-    bool get_transformation (float (&matrix)[4], contour_point_t &trans) const
-    {
-      matrix[0] = matrix[3] = 1.f;
-      matrix[1] = matrix[2] = 0.f;
-
-      int tx, ty;
-      const HBINT8 *p = &StructAfter<const HBINT8> (glyphIndex);
-      if (flags & ARG_1_AND_2_ARE_WORDS)
-      {
-	tx = *(const HBINT16 *) p;
-	p += HBINT16::static_size;
-	ty = *(const HBINT16 *) p;
-	p += HBINT16::static_size;
-      }
-      else
-      {
-	tx = *p++;
-	ty = *p++;
-      }
-      if (is_anchored ()) tx = ty = 0;
-
-      trans.init ((float) tx, (float) ty);
-
-      {
-	const F2DOT14 *points = (const F2DOT14 *) p;
-	if (flags & WE_HAVE_A_SCALE)
-	{
-	  matrix[0] = matrix[3] = points[0].to_float ();
-	  return true;
-	}
-	else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
-	{
-	  matrix[0] = points[0].to_float ();
-	  matrix[3] = points[1].to_float ();
-	  return true;
-	}
-	else if (flags & WE_HAVE_A_TWO_BY_TWO)
-	{
-	  matrix[0] = points[0].to_float ();
-	  matrix[1] = points[1].to_float ();
-	  matrix[2] = points[2].to_float ();
-	  matrix[3] = points[3].to_float ();
-	  return true;
-	}
-      }
-      return tx || ty;
-    }
-
-    protected:
-    HBUINT16	flags;
-    HBGlyphID16	glyphIndex;
-    public:
-    DEFINE_SIZE_MIN (4);
-  };
-
-  struct composite_iter_t : hb_iter_with_fallback_t<composite_iter_t, const CompositeGlyphChain &>
-  {
-    typedef const CompositeGlyphChain *__item_t__;
-    composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) :
-        glyph (glyph_), current (nullptr), current_size (0)
-    {
-      set_next (current_);
-    }
-
-    composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {}
-
-    const CompositeGlyphChain &__item__ () const { return *current; }
-    bool __more__ () const { return current; }
-    void __next__ ()
-    {
-      if (!current->has_more ()) { current = nullptr; return; }
-
-      set_next (&StructAtOffset<CompositeGlyphChain> (current, current_size));
-    }
-    bool operator != (const composite_iter_t& o) const
-    { return glyph != o.glyph || current != o.current; }
-
-
-    void set_next (const CompositeGlyphChain *composite)
-    {
-      if (!glyph.check_range (composite, CompositeGlyphChain::min_size))
-      {
-        current = nullptr;
-        current_size = 0;
-        return;
-      }
-      unsigned size = composite->get_size ();
-      if (!glyph.check_range (composite, size))
-      {
-        current = nullptr;
-        current_size = 0;
-        return;
-      }
-
-      current = composite;
-      current_size = size;
-    }
-
-    private:
-    hb_bytes_t glyph;
-    __item_t__ current;
-    unsigned current_size;
-  };
-
-  enum phantom_point_index_t
-  {
-    PHANTOM_LEFT   = 0,
-    PHANTOM_RIGHT  = 1,
-    PHANTOM_TOP    = 2,
-    PHANTOM_BOTTOM = 3,
-    PHANTOM_COUNT  = 4
-  };
-
-  struct accelerator_t;
-
-  struct Glyph
-  {
-    enum simple_glyph_flag_t
-    {
-      FLAG_ON_CURVE       = 0x01,
-      FLAG_X_SHORT        = 0x02,
-      FLAG_Y_SHORT        = 0x04,
-      FLAG_REPEAT         = 0x08,
-      FLAG_X_SAME         = 0x10,
-      FLAG_Y_SAME         = 0x20,
-      FLAG_OVERLAP_SIMPLE = 0x40,
-      FLAG_RESERVED2      = 0x80
-    };
-
-    private:
-    struct GlyphHeader
-    {
-      bool has_data () const { return numberOfContours; }
-
-      bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator,
-		        hb_codepoint_t gid, hb_glyph_extents_t *extents) const
-      {
-	/* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */
-	/* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
-	extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid));
-	extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax));
-	extents->width     = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax));
-	extents->height    = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax));
-
-	return true;
-      }
-
-      HBINT16	numberOfContours;
-			/* If the number of contours is
-			 * greater than or equal to zero,
-			 * this is a simple glyph; if negative,
-			 * this is a composite glyph. */
-      FWORD	xMin;	/* Minimum x for coordinate data. */
-      FWORD	yMin;	/* Minimum y for coordinate data. */
-      FWORD	xMax;	/* Maximum x for coordinate data. */
-      FWORD	yMax;	/* Maximum y for coordinate data. */
-      public:
-      DEFINE_SIZE_STATIC (10);
-    };
-
-    struct SimpleGlyph
-    {
-      const GlyphHeader &header;
-      hb_bytes_t bytes;
-      SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
-	header (header_), bytes (bytes_) {}
-
-      unsigned int instruction_len_offset () const
-      { return GlyphHeader::static_size + 2 * header.numberOfContours; }
-
-      unsigned int length (unsigned int instruction_len) const
-      { return instruction_len_offset () + 2 + instruction_len; }
-
-      unsigned int instructions_length () const
-      {
-	unsigned int instruction_length_offset = instruction_len_offset ();
-	if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0;
-
-	const HBUINT16 &instructionLength = StructAtOffset<HBUINT16> (&bytes, instruction_length_offset);
-	/* Out of bounds of the current glyph */
-	if (unlikely (length (instructionLength) > bytes.length)) return 0;
-	return instructionLength;
-      }
-
-      const Glyph trim_padding () const
-      {
-	/* based on FontTools _g_l_y_f.py::trim */
-	const uint8_t *glyph = (uint8_t*) bytes.arrayZ;
-	const uint8_t *glyph_end = glyph + bytes.length;
-	/* simple glyph w/contours, possibly trimmable */
-	glyph += instruction_len_offset ();
-
-	if (unlikely (glyph + 2 >= glyph_end)) return Glyph ();
-	unsigned int num_coordinates = StructAtOffset<HBUINT16> (glyph - 2, 0) + 1;
-	unsigned int num_instructions = StructAtOffset<HBUINT16> (glyph, 0);
-
-	glyph += 2 + num_instructions;
-
-	unsigned int coord_bytes = 0;
-	unsigned int coords_with_flags = 0;
-	while (glyph < glyph_end)
-	{
-	  uint8_t flag = *glyph;
-	  glyph++;
-
-	  unsigned int repeat = 1;
-	  if (flag & FLAG_REPEAT)
-	  {
-	    if (unlikely (glyph >= glyph_end)) return Glyph ();
-	    repeat = *glyph + 1;
-	    glyph++;
-	  }
-
-	  unsigned int xBytes, yBytes;
-	  xBytes = yBytes = 0;
-	  if (flag & FLAG_X_SHORT) xBytes = 1;
-	  else if ((flag & FLAG_X_SAME) == 0) xBytes = 2;
-
-	  if (flag & FLAG_Y_SHORT) yBytes = 1;
-	  else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2;
-
-	  coord_bytes += (xBytes + yBytes) * repeat;
-	  coords_with_flags += repeat;
-	  if (coords_with_flags >= num_coordinates) break;
-	}
-
-	if (unlikely (coords_with_flags != num_coordinates)) return Glyph ();
-	return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph)));
-      }
-
-      /* zero instruction length */
-      void drop_hints ()
-      {
-	GlyphHeader &glyph_header = const_cast<GlyphHeader &> (header);
-	(HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0;
-      }
-
-      void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
-      {
-	unsigned int instructions_len = instructions_length ();
-	unsigned int glyph_length = length (instructions_len);
-	dest_start = bytes.sub_array (0, glyph_length - instructions_len);
-	dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length);
-      }
-
-      void set_overlaps_flag ()
-      {
-        if (unlikely (!header.numberOfContours)) return;
-
-        unsigned flags_offset = length (instructions_length ());
-        if (unlikely (flags_offset + 1 > bytes.length)) return;
-
-	HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset<HBUINT16> (&bytes, flags_offset);
-        first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE;
-      }
-
-      static bool read_points (const HBUINT8 *&p /* IN/OUT */,
-			       contour_point_vector_t &points_ /* IN/OUT */,
-			       const hb_bytes_t &bytes,
-			       void (* setter) (contour_point_t &_, float v),
-			       const simple_glyph_flag_t short_flag,
-			       const simple_glyph_flag_t same_flag)
-      {
-	float v = 0;
-	for (unsigned i = 0; i < points_.length; i++)
-	{
-	  uint8_t flag = points_[i].flag;
-	  if (flag & short_flag)
-	  {
-	    if (unlikely (!bytes.check_range (p))) return false;
-	    if (flag & same_flag)
-	      v += *p++;
-	    else
-	      v -= *p++;
-	  }
-	  else
-	  {
-	    if (!(flag & same_flag))
-	    {
-	      if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false;
-	      v += *(const HBINT16 *) p;
-	      p += HBINT16::static_size;
-	    }
-	  }
-	  setter (points_[i], v);
-	}
-	return true;
-      }
-
-      bool get_contour_points (contour_point_vector_t &points_ /* OUT */,
-			       bool phantom_only = false) const
-      {
-	const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header);
-	int num_contours = header.numberOfContours;
-	if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false;
-	unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
-
-	points_.resize (num_points);
-	for (unsigned int i = 0; i < points_.length; i++) points_[i].init ();
-	if (phantom_only) return true;
-
-	for (int i = 0; i < num_contours; i++)
-	  points_[endPtsOfContours[i]].is_end_point = true;
-
-	/* Skip instructions */
-	const HBUINT8 *p = &StructAtOffset<HBUINT8> (&endPtsOfContours[num_contours + 1],
-						     endPtsOfContours[num_contours]);
-
-	/* Read flags */
-	for (unsigned int i = 0; i < num_points; i++)
-	{
-	  if (unlikely (!bytes.check_range (p))) return false;
-	  uint8_t flag = *p++;
-	  points_[i].flag = flag;
-	  if (flag & FLAG_REPEAT)
-	  {
-	    if (unlikely (!bytes.check_range (p))) return false;
-	    unsigned int repeat_count = *p++;
-	    while ((repeat_count-- > 0) && (++i < num_points))
-	      points_[i].flag = flag;
-	  }
-	}
-
-	/* Read x & y coordinates */
-	return read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.x = v; },
-			    FLAG_X_SHORT, FLAG_X_SAME)
-	    && read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.y = v; },
-			    FLAG_Y_SHORT, FLAG_Y_SAME);
-      }
-    };
-
-    struct CompositeGlyph
-    {
-      const GlyphHeader &header;
-      hb_bytes_t bytes;
-      CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
-	header (header_), bytes (bytes_) {}
-
-      composite_iter_t get_iterator () const
-      { return composite_iter_t (bytes, &StructAfter<CompositeGlyphChain, GlyphHeader> (header)); }
-
-      unsigned int instructions_length (hb_bytes_t bytes) const
-      {
-	unsigned int start = bytes.length;
-	unsigned int end = bytes.length;
-	const CompositeGlyphChain *last = nullptr;
-	for (auto &item : get_iterator ())
-	  last = &item;
-	if (unlikely (!last)) return 0;
-
-	if (last->has_instructions ())
-	  start = (char *) last - &bytes + last->get_size ();
-	if (unlikely (start > end)) return 0;
-	return end - start;
-      }
-
-      /* Trimming for composites not implemented.
-       * If removing hints it falls out of that. */
-      const Glyph trim_padding () const { return Glyph (bytes); }
-
-      void drop_hints ()
-      {
-	for (const auto &_ : get_iterator ())
-	  const_cast<CompositeGlyphChain &> (_).drop_instructions_flag ();
-      }
-
-      /* Chop instructions off the end */
-      void drop_hints_bytes (hb_bytes_t &dest_start) const
-      { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); }
-
-      void set_overlaps_flag ()
-      {
-        const_cast<CompositeGlyphChain &> (StructAfter<CompositeGlyphChain, GlyphHeader> (header))
-                .set_overlaps_flag ();
-      }
-    };
-
-    enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE };
-
-    public:
-    composite_iter_t get_composite_iterator () const
-    {
-      if (type != COMPOSITE) return composite_iter_t ();
-      return CompositeGlyph (*header, bytes).get_iterator ();
-    }
-
-    const Glyph trim_padding () const
-    {
-      switch (type) {
-      case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
-      case SIMPLE:    return SimpleGlyph (*header, bytes).trim_padding ();
-      default:        return bytes;
-      }
-    }
-
-    void drop_hints ()
-    {
-      switch (type) {
-      case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
-      case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints (); return;
-      default:        return;
-      }
-    }
-
-    void set_overlaps_flag ()
-    {
-      switch (type) {
-      case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
-      case SIMPLE:    SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
-      default:        return;
-      }
-    }
-
-    void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
-    {
-      switch (type) {
-      case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
-      case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
-      default:        return;
-      }
-    }
-
-    /* Note: Recursively calls itself.
-     * all_points includes phantom points
-     */
-    bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
-		     contour_point_vector_t &all_points /* OUT */,
-		     bool phantom_only = false,
-		     unsigned int depth = 0) const
-    {
-      if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
-      contour_point_vector_t points;
-
-      switch (type) {
-      case COMPOSITE:
-      {
-	/* pseudo component points for each component in composite glyph */
-	unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ());
-	if (unlikely (!points.resize (num_points))) return false;
-	for (unsigned i = 0; i < points.length; i++)
-	  points[i].init ();
-	break;
-      }
-      case SIMPLE:
-	if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
-	  return false;
-	break;
-      }
-
-      /* Init phantom points */
-      if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
-      hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
-      {
-	for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init ();
-	int h_delta = (int) header->xMin -
-		      glyf_accelerator.hmtx->get_side_bearing (gid);
-	int v_orig  = (int) header->yMax +
-#ifndef HB_NO_VERTICAL
-		      glyf_accelerator.vmtx->get_side_bearing (gid)
-#else
-		      0
-#endif
-		      ;
-	unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid);
-	unsigned v_adv =
-#ifndef HB_NO_VERTICAL
-			 glyf_accelerator.vmtx->get_advance (gid)
-#else
-			 - font->face->get_upem ()
-#endif
-			 ;
-	phantoms[PHANTOM_LEFT].x = h_delta;
-	phantoms[PHANTOM_RIGHT].x = h_adv + h_delta;
-	phantoms[PHANTOM_TOP].y = v_orig;
-	phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
-      }
-
-#ifndef HB_NO_VAR
-      glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ());
-#endif
-
-      switch (type) {
-      case SIMPLE:
-	all_points.extend (points.as_array ());
-	break;
-      case COMPOSITE:
-      {
-	unsigned int comp_index = 0;
-	for (auto &item : get_composite_iterator ())
-	{
-	  contour_point_vector_t comp_points;
-	  if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_glyph_index ())
-					 .get_points (font, glyf_accelerator, comp_points,
-			 			      phantom_only, depth + 1)
-			|| comp_points.length < PHANTOM_COUNT))
-	    return false;
-
-	  /* Copy phantom points from component if USE_MY_METRICS flag set */
-	  if (item.is_use_my_metrics ())
-	    for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
-	      phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
-
-	  /* Apply component transformation & translation */
-	  item.transform_points (comp_points);
-
-	  /* Apply translation from gvar */
-	  comp_points.translate (points[comp_index]);
-
-	  if (item.is_anchored ())
-	  {
-	    unsigned int p1, p2;
-	    item.get_anchor_points (p1, p2);
-	    if (likely (p1 < all_points.length && p2 < comp_points.length))
-	    {
-	      contour_point_t delta;
-	      delta.init (all_points[p1].x - comp_points[p2].x,
-			  all_points[p1].y - comp_points[p2].y);
-
-	      comp_points.translate (delta);
-	    }
-	  }
-
-	  all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT));
-
-	  comp_index++;
-	}
-
-	all_points.extend (phantoms);
-      } break;
-      default:
-	all_points.extend (phantoms);
-      }
-
-      if (depth == 0) /* Apply at top level */
-      {
-	/* Undocumented rasterizer behavior:
-	 * Shift points horizontally by the updated left side bearing
-	 */
-	contour_point_t delta;
-	delta.init (-phantoms[PHANTOM_LEFT].x, 0.f);
-	if (delta.x) all_points.translate (delta);
-      }
-
-      return true;
-    }
-
-    bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator,
-		      hb_glyph_extents_t *extents) const
-    {
-      if (type == EMPTY) return true; /* Empty glyph; zero extents. */
-      return header->get_extents (font, glyf_accelerator, gid, extents);
-    }
-
-    hb_bytes_t get_bytes () const { return bytes; }
-
-    Glyph (hb_bytes_t bytes_ = hb_bytes_t (),
-	   hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_),
-							header (bytes.as<GlyphHeader> ())
-    {
-      int num_contours = header->numberOfContours;
-      if (unlikely (num_contours == 0)) type = EMPTY;
-      else if (num_contours > 0) type = SIMPLE;
-      else type = COMPOSITE; /* negative numbers */
-    }
-
-    protected:
-    hb_bytes_t bytes;
-    hb_codepoint_t gid;
-    const GlyphHeader *header;
-    unsigned type;
-  };
-
-  struct accelerator_t
-  {
-    accelerator_t (hb_face_t *face)
-    {
-      short_offset = false;
-      num_glyphs = 0;
-      loca_table = nullptr;
-      glyf_table = nullptr;
-#ifndef HB_NO_VAR
-      gvar = nullptr;
-#endif
-      hmtx = nullptr;
-#ifndef HB_NO_VERTICAL
-      vmtx = nullptr;
-#endif
-      const OT::head &head = *face->table.head;
-      if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0)
-	/* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
-	return;
-      short_offset = 0 == head.indexToLocFormat;
-
-      loca_table = face->table.loca.get_blob (); // Needs no destruct!
-      glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
-#ifndef HB_NO_VAR
-      gvar = face->table.gvar;
-#endif
-      hmtx = face->table.hmtx;
-#ifndef HB_NO_VERTICAL
-      vmtx = face->table.vmtx;
-#endif
-
-      num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
-      num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
-    }
-    ~accelerator_t ()
-    {
-      glyf_table.destroy ();
-    }
-
-    protected:
-    template<typename T>
-    bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const
-    {
-      if (gid >= num_glyphs) return false;
-
-      /* Making this allocfree is not that easy
-	 https://github.com/harfbuzz/harfbuzz/issues/2095
-	 mostly because of gvar handling in VF fonts,
-	 perhaps a separate path for non-VF fonts can be considered */
-      contour_point_vector_t all_points;
-
-      bool phantom_only = !consumer.is_consuming_contour_points ();
-      if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only)))
-	return false;
-
-      if (consumer.is_consuming_contour_points ())
-      {
-	for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index)
-	  consumer.consume_point (all_points[point_index]);
-	consumer.points_end ();
-      }
-
-      /* Where to write phantoms, nullptr if not requested */
-      contour_point_t *phantoms = consumer.get_phantoms_sink ();
-      if (phantoms)
-	for (unsigned i = 0; i < PHANTOM_COUNT; ++i)
-	  phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i];
-
-      return true;
-    }
-
-#ifndef HB_NO_VAR
-    struct points_aggregator_t
-    {
-      hb_font_t *font;
-      hb_glyph_extents_t *extents;
-      contour_point_t *phantoms;
-
-      struct contour_bounds_t
-      {
-	contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
-
-	void add (const contour_point_t &p)
-	{
-	  min_x = hb_min (min_x, p.x);
-	  min_y = hb_min (min_y, p.y);
-	  max_x = hb_max (max_x, p.x);
-	  max_y = hb_max (max_y, p.y);
-	}
-
-	bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
-
-	void get_extents (hb_font_t *font, hb_glyph_extents_t *extents)
-	{
-	  if (unlikely (empty ()))
-	  {
-	    extents->width = 0;
-	    extents->x_bearing = 0;
-	    extents->height = 0;
-	    extents->y_bearing = 0;
-	    return;
-	  }
-	  extents->x_bearing = font->em_scalef_x (min_x);
-	  extents->width = font->em_scalef_x (max_x) - extents->x_bearing;
-	  extents->y_bearing = font->em_scalef_y (max_y);
-	  extents->height = font->em_scalef_y (min_y) - extents->y_bearing;
-	}
-
-	protected:
-	float min_x, min_y, max_x, max_y;
-      } bounds;
-
-      points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_)
-      {
-	font = font_;
-	extents = extents_;
-	phantoms = phantoms_;
-	if (extents) bounds = contour_bounds_t ();
-      }
-
-      void consume_point (const contour_point_t &point) { bounds.add (point); }
-      void points_end () { bounds.get_extents (font, extents); }
-
-      bool is_consuming_contour_points () { return extents; }
-      contour_point_t *get_phantoms_sink () { return phantoms; }
-    };
-
-    public:
-    unsigned
-    get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
-    {
-      if (unlikely (gid >= num_glyphs)) return 0;
-
-      bool success = false;
-
-      contour_point_t phantoms[PHANTOM_COUNT];
-      if (likely (font->num_coords == gvar->get_axis_count ()))
-	success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms));
-
-      if (unlikely (!success))
-	return
-#ifndef HB_NO_VERTICAL
-	  is_vertical ? vmtx->get_advance (gid) :
-#endif
-	  hmtx->get_advance (gid);
-
-      float result = is_vertical
-		   ? phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y
-		   : phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x;
-      return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
-    }
-
-    int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
-    {
-      if (unlikely (gid >= num_glyphs)) return 0;
-
-      hb_glyph_extents_t extents;
-
-      contour_point_t phantoms[PHANTOM_COUNT];
-      if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms))))
-	return
-#ifndef HB_NO_VERTICAL
-	  is_vertical ? vmtx->get_side_bearing (gid) :
-#endif
-	  hmtx->get_side_bearing (gid);
-
-      return is_vertical
-	   ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing
-	   : floorf (phantoms[PHANTOM_LEFT].x);
-    }
-#endif
-
-    public:
-    bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
-    {
-      if (unlikely (gid >= num_glyphs)) return false;
-
-#ifndef HB_NO_VAR
-      if (font->num_coords && font->num_coords == gvar->get_axis_count ())
-	return get_points (font, gid, points_aggregator_t (font, extents, nullptr));
-#endif
-      return glyph_for_gid (gid).get_extents (font, *this, extents);
-    }
-
-    const Glyph
-    glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
-    {
-      if (unlikely (gid >= num_glyphs)) return Glyph ();
-
-      unsigned int start_offset, end_offset;
-
-      if (short_offset)
-      {
-	const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
-	start_offset = 2 * offsets[gid];
-	end_offset   = 2 * offsets[gid + 1];
-      }
-      else
-      {
-	const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
-	start_offset = offsets[gid];
-	end_offset   = offsets[gid + 1];
-      }
-
-      if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
-	return Glyph ();
-
-      Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
-			       end_offset - start_offset), gid);
-      return needs_padding_removal ? glyph.trim_padding () : glyph;
-    }
-
-    unsigned
-    add_gid_and_children (hb_codepoint_t gid,
-			  hb_set_t *gids_to_retain,
-			  unsigned depth = 0,
-			  unsigned operation_count = 0) const
-    {
-      if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
-      if (unlikely (operation_count++ > HB_MAX_COMPOSITE_OPERATIONS)) return operation_count;
-      /* Check if is already visited */
-      if (gids_to_retain->has (gid)) return operation_count;
-
-      gids_to_retain->add (gid);
-
-      auto it = glyph_for_gid (gid).get_composite_iterator ();
-      while (it)
-      {
-        auto item = *(it++);
-        operation_count =
-            add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth, operation_count);
-      }
-
-      return operation_count;
-    }
-
-    struct path_builder_t
-    {
-      hb_font_t *font;
-      hb_draw_session_t *draw_session;
-
-      struct optional_point_t
-      {
-	optional_point_t () { has_data = false; }
-	optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; }
-
-	bool has_data;
-	float x;
-	float y;
-
-	optional_point_t lerp (optional_point_t p, float t)
-	{ return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
-      } first_oncurve, first_offcurve, last_offcurve;
-
-      path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
-      {
-	font = font_;
-	draw_session = &draw_session_;
-	first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
-      }
-
-      /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
-	 See also:
-	 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
-	 * https://stackoverflow.com/a/20772557 */
-      void consume_point (const contour_point_t &point)
-      {
-	bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE;
-	optional_point_t p (point.x, point.y);
-	if (!first_oncurve.has_data)
-	{
-	  if (is_on_curve)
-	  {
-	    first_oncurve = p;
-	    draw_session->move_to (font->em_fscalef_x (p.x), font->em_fscalef_y (p.y));
-	  }
-	  else
-	  {
-	    if (first_offcurve.has_data)
-	    {
-	      optional_point_t mid = first_offcurve.lerp (p, .5f);
-	      first_oncurve = mid;
-	      last_offcurve = p;
-	      draw_session->move_to (font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y));
-	    }
-	    else
-	      first_offcurve = p;
-	  }
-	}
-	else
-	{
-	  if (last_offcurve.has_data)
-	  {
-	    if (is_on_curve)
-	    {
-	      draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
-					 font->em_fscalef_x (p.x), font->em_fscalef_y (p.y));
-	      last_offcurve = optional_point_t ();
-	    }
-	    else
-	    {
-	      optional_point_t mid = last_offcurve.lerp (p, .5f);
-	      draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
-					 font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y));
-	      last_offcurve = p;
-	    }
-	  }
-	  else
-	  {
-	    if (is_on_curve)
-	      draw_session->line_to (font->em_fscalef_x (p.x), font->em_fscalef_y (p.y));
-	    else
-	      last_offcurve = p;
-	  }
-	}
-
-	if (point.is_end_point)
-	{
-	  if (first_offcurve.has_data && last_offcurve.has_data)
-	  {
-	    optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
-	    draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
-				       font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y));
-	    last_offcurve = optional_point_t ();
-	    /* now check the rest */
-	  }
-
-	  if (first_offcurve.has_data && first_oncurve.has_data)
-	    draw_session->quadratic_to (font->em_fscalef_x (first_offcurve.x), font->em_fscalef_y (first_offcurve.y),
-				       font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y));
-	  else if (last_offcurve.has_data && first_oncurve.has_data)
-	    draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
-				       font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y));
-	  else if (first_oncurve.has_data)
-	    draw_session->line_to (font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y));
-	  else if (first_offcurve.has_data)
-	  {
-	    float x = font->em_fscalef_x (first_offcurve.x), y = font->em_fscalef_x (first_offcurve.y);
-	    draw_session->move_to (x, y);
-	    draw_session->quadratic_to (x, y, x, y);
-	  }
-
-	  /* Getting ready for the next contour */
-	  first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
-	  draw_session->close_path ();
-	}
-      }
-      void points_end () {}
-
-      bool is_consuming_contour_points () { return true; }
-      contour_point_t *get_phantoms_sink () { return nullptr; }
-    };
-
-    bool
-    get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
-    { return get_points (font, gid, path_builder_t (font, draw_session)); }
-
-#ifndef HB_NO_VAR
-    const gvar_accelerator_t *gvar;
-#endif
-    const hmtx_accelerator_t *hmtx;
-#ifndef HB_NO_VERTICAL
-    const vmtx_accelerator_t *vmtx;
-#endif
-
-    private:
-    bool short_offset;
-    unsigned int num_glyphs;
-    hb_blob_ptr_t<loca> loca_table;
-    hb_blob_ptr_t<glyf> glyf_table;
-  };
-
-  struct SubsetGlyph
-  {
-    hb_codepoint_t new_gid;
-    hb_codepoint_t old_gid;
-    Glyph source_glyph;
-    hb_bytes_t dest_start;  /* region of source_glyph to copy first */
-    hb_bytes_t dest_end;    /* region of source_glyph to copy second */
-
-    bool serialize (hb_serialize_context_t *c,
-                    bool use_short_loca,
-		    const hb_subset_plan_t *plan) const
-    {
-      TRACE_SERIALIZE (this);
-
-      hb_bytes_t dest_glyph = dest_start.copy (c);
-      dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length);
-      unsigned int pad_length = use_short_loca ? padding () : 0;
-      DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
-
-      HBUINT8 pad;
-      pad = 0;
-      while (pad_length > 0)
-      {
-	c->embed (pad);
-	pad_length--;
-      }
-
-      if (unlikely (!dest_glyph.length)) return_trace (true);
-
-      /* update components gids */
-      for (auto &_ : Glyph (dest_glyph).get_composite_iterator ())
-      {
-	hb_codepoint_t new_gid;
-	if (plan->new_gid_for_old_gid (_.get_glyph_index (), &new_gid))
-	  const_cast<CompositeGlyphChain &> (_).set_glyph_index (new_gid);
-      }
-
-      if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
-        Glyph (dest_glyph).drop_hints ();
-
-      if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG)
-        Glyph (dest_glyph).set_overlaps_flag ();
-
-      return_trace (true);
-    }
-
-    void drop_hints_bytes ()
-    { source_glyph.drop_hints_bytes (dest_start, dest_end); }
-
-    unsigned int      length () const { return dest_start.length + dest_end.length; }
-    /* pad to 2 to ensure 2-byte loca will be ok */
-    unsigned int     padding () const { return length () % 2; }
-    unsigned int padded_size () const { return length () + padding (); }
-  };
-
-  protected:
-  UnsizedArrayOf<HBUINT8>
-		dataZ;	/* Glyphs data. */
-  public:
-  DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
-			 * check the size externally, allow Null() object of it by
-			 * defining it _MIN instead. */
-};
-
-struct glyf_accelerator_t : glyf::accelerator_t {
-  glyf_accelerator_t (hb_face_t *face) : glyf::accelerator_t (face) {}
-};
-
-
-} /* namespace OT */
-
+#include "OT/glyf/glyf.hh"
 
 #endif /* HB_OT_GLYF_TABLE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hmtx-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hmtx-table.hh
index d5e1fc91d25ab44b2c03afcf49fc10813403fca4..d0e46e0b0fcf045458662a618e282a0719b0ec4b 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hmtx-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hmtx-table.hh
@@ -242,7 +242,7 @@ struct hmtxvmtx
 	return side_bearing;
 
       if (var_table.get_length ())
-	return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
+	return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords);
 
       return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
 #else
@@ -284,7 +284,8 @@ struct hmtxvmtx
     }
 
     unsigned int get_advance (hb_codepoint_t  glyph,
-			      hb_font_t      *font) const
+			      hb_font_t      *font,
+			      VariationStore::cache_t *store_cache = nullptr) const
     {
       unsigned int advance = get_advance (glyph);
 
@@ -293,7 +294,7 @@ struct hmtxvmtx
 	return advance;
 
       if (var_table.get_length ())
-	return advance + roundf (var_table->get_advance_var (glyph, font)); // TODO Optimize?!
+	return advance + roundf (var_table->get_advance_var (glyph, font, store_cache)); // TODO Optimize?!
 
       return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
 #else
@@ -310,7 +311,7 @@ struct hmtxvmtx
 
     unsigned int default_advance;
 
-    private:
+    public:
     hb_blob_ptr_t<hmtxvmtx> table;
     hb_blob_ptr_t<HVARVVAR> var_table;
   };
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh
index f2a58028e32bf5d1818582a280960369c93fefa2..d3438053468e09b1791ff007b1d54fa16b8a1149 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh
@@ -91,18 +91,18 @@ template<typename Iterator>
 static inline void ClassDef_serialize (hb_serialize_context_t *c,
 				       Iterator it);
 
-static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
-					  const hb_map_t &gid_klass_map,
-					  hb_sorted_vector_t<HBGlyphID16> &glyphs,
-					  const hb_set_t &klasses,
-					  bool use_class_zero,
-					  hb_map_t *klass_map /*INOUT*/);
+static void ClassDef_remap_and_serialize (
+    hb_serialize_context_t *c,
+    const hb_set_t &klasses,
+    bool use_class_zero,
+    hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+    hb_map_t *klass_map /*IN/OUT*/);
 
 
 struct hb_prune_langsys_context_t
 {
   hb_prune_langsys_context_t (const void         *table_,
-                              hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map_,
+                              hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map_,
                               const hb_map_t     *duplicate_feature_map_,
                               hb_set_t           *new_collected_feature_indexes_)
       :table (table_),
@@ -122,7 +122,7 @@ struct hb_prune_langsys_context_t
 
   public:
   const void *table;
-  hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map;
+  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map;
   const hb_map_t     *duplicate_feature_map;
   hb_set_t           *new_feature_indexes;
 
@@ -162,14 +162,14 @@ struct hb_subset_layout_context_t :
   hb_subset_context_t *subset_context;
   const hb_tag_t table_tag;
   const hb_map_t *lookup_index_map;
-  const hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map;
+  const hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map;
   const hb_map_t *feature_index_map;
   unsigned cur_script_index;
 
   hb_subset_layout_context_t (hb_subset_context_t *c_,
 			      hb_tag_t tag_,
 			      hb_map_t *lookup_map_,
-			      hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map_,
+			      hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map_,
 			      hb_map_t *feature_index_map_) :
 				subset_context (c_),
 				table_tag (tag_),
@@ -659,7 +659,8 @@ struct LangSys
     auto *out = c->serializer->start_embed (*this);
     if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
 
-    out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex) ? l->feature_index_map->get (reqFeatureIndex) : 0xFFFFu;
+    const unsigned *v;
+    out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu;
 
     if (!l->visitFeatureIndex (featureIndex.len))
       return_trace (false);
@@ -722,12 +723,8 @@ struct Script
 
     if (!c->script_langsys_map->has (script_index))
     {
-      hb_set_t* empty_set = hb_set_create ();
-      if (unlikely (!c->script_langsys_map->set (script_index, empty_set)))
-      {
-	hb_set_destroy (empty_set);
+      if (unlikely (!c->script_langsys_map->set (script_index, hb::unique_ptr<hb_set_t> {hb_set_create ()})))
 	return;
-      }
     }
 
     unsigned langsys_count = get_lang_sys_count ();
@@ -1470,7 +1467,8 @@ struct CoverageFormat1
     void next () { i++; }
     hb_codepoint_t get_glyph () const { return c->glyphArray[i]; }
     bool operator != (const iter_t& o) const
-    { return i != o.i || c != o.c; }
+    { return i != o.i; }
+    iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; }
 
     private:
     const struct CoverageFormat1 *c;
@@ -1506,12 +1504,6 @@ struct CoverageFormat2
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (this))) return_trace (false);
 
-    if (unlikely (!glyphs))
-    {
-      rangeRecord.len = 0;
-      return_trace (true);
-    }
-
     /* TODO(iter) Write more efficiently? */
 
     unsigned num_ranges = 0;
@@ -1524,6 +1516,7 @@ struct CoverageFormat2
     }
 
     if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false);
+    if (!num_ranges) return_trace (true);
 
     unsigned count = 0;
     unsigned range = (unsigned) -1;
@@ -1552,25 +1545,26 @@ struct CoverageFormat2
 
   bool intersects (const hb_set_t *glyphs) const
   {
-    /* TODO Speed up, using hb_set_next() and bsearch()? */
-    /* TODO(iter) Rewrite as dagger. */
-    for (const auto& range : rangeRecord.as_array ())
-      if (range.intersects (glyphs))
-	return true;
-    return false;
+    return hb_any (+ hb_iter (rangeRecord.as_array ())
+		   | hb_map ([glyphs] (const RangeRecord &range) { return range.intersects (glyphs); }));
   }
   bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   {
-    /* TODO(iter) Rewrite as dagger. */
-    for (const auto& range : rangeRecord.as_array ())
+    auto cmp = [] (const void *pk, const void *pr) -> int
     {
-      if (range.value <= index &&
-	  index < (unsigned int) range.value + (range.last - range.first) &&
-	  range.intersects (glyphs))
-	return true;
-      else if (index < range.value)
-	return false;
-    }
+      unsigned index = * (const unsigned *) pk;
+      const RangeRecord &range = * (const RangeRecord *) pr;
+      if (index < range.value) return -1;
+      if (index > (unsigned int) range.value + (range.last - range.first)) return +1;
+      return 0;
+    };
+
+    auto arr = rangeRecord.as_array ();
+    unsigned idx;
+    if (hb_bsearch_impl (&idx, index,
+			 arr.arrayZ, arr.length, sizeof (arr[0]),
+			 (int (*)(const void *_key, const void *_item)) cmp))
+      return arr.arrayZ[idx].intersects (glyphs);
     return false;
   }
 
@@ -1579,8 +1573,10 @@ struct CoverageFormat2
     for (const auto& range : rangeRecord.as_array ())
     {
       if (!range.intersects (glyphs)) continue;
-      for (hb_codepoint_t g = range.first; g <= range.last; g++)
-        if (glyphs->has (g)) intersect_glyphs->add (g);
+      unsigned last = range.last;
+      for (hb_codepoint_t g = range.first - 1;
+	   glyphs->next (&g) && g <= last;)
+        intersect_glyphs->add (g);
     }
   }
 
@@ -1632,6 +1628,8 @@ struct CoverageFormat2
 	   return;
 	  }
 	}
+	else
+	  j = 0;
 	return;
       }
       coverage++;
@@ -1639,7 +1637,15 @@ struct CoverageFormat2
     }
     hb_codepoint_t get_glyph () const { return j; }
     bool operator != (const iter_t& o) const
-    { return i != o.i || j != o.j || c != o.c; }
+    { return i != o.i || j != o.j; }
+    iter_t __end__ () const
+    {
+      iter_t it;
+      it.init (*c);
+      it.i = c->rangeRecord.len;
+      it.j = 0;
+      return it;
+    }
 
     private:
     const struct CoverageFormat2 *c;
@@ -1708,18 +1714,17 @@ struct Coverage
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
-
     auto it =
     + iter ()
-    | hb_filter (glyphset)
-    | hb_map_retains_sorting (glyph_map)
+    | hb_filter (c->plan->glyph_map_gsub)
+    | hb_map_retains_sorting (c->plan->glyph_map_gsub)
     ;
 
-    bool ret = bool (it);
-    Coverage_serialize (c->serializer, it);
-    return_trace (ret);
+    // Cache the iterator result as it will be iterated multiple times
+    // by the serialize code below.
+    hb_sorted_vector_t<hb_codepoint_t> glyphs (it);
+    Coverage_serialize (c->serializer, glyphs.iter ());
+    return_trace (bool (glyphs));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -1822,7 +1827,7 @@ struct Coverage
     }
     bool operator != (const iter_t& o) const
     {
-      if (format != o.format) return true;
+      if (unlikely (format != o.format)) return true;
       switch (format)
       {
       case 1: return u.format1 != o.u.format1;
@@ -1830,6 +1835,18 @@ struct Coverage
       default:return false;
       }
     }
+    iter_t __end__ () const
+    {
+      iter_t it = {};
+      it.format = format;
+      switch (format)
+      {
+      case 1: it.u.format1 = u.format1.__end__ (); break;
+      case 2: it.u.format2 = u.format2.__end__ (); break;
+      default: break;
+      }
+      return it;
+    }
 
     private:
     unsigned int format;
@@ -1857,16 +1874,14 @@ Coverage_serialize (hb_serialize_context_t *c,
 { c->start_embed<Coverage> ()->serialize (c, it); }
 
 static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
-					  const hb_map_t &gid_klass_map,
-					  hb_sorted_vector_t<HBGlyphID16> &glyphs,
 					  const hb_set_t &klasses,
                                           bool use_class_zero,
-					  hb_map_t *klass_map /*INOUT*/)
+                                          hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+					  hb_map_t *klass_map /*IN/OUT*/)
 {
   if (!klass_map)
   {
-    ClassDef_serialize (c, hb_zip (glyphs.iter (), + glyphs.iter ()
-						   | hb_map (gid_klass_map)));
+    ClassDef_serialize (c, glyph_and_klass.iter ());
     return;
   }
 
@@ -1883,17 +1898,15 @@ static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
     idx++;
   }
 
-  auto it =
-  + glyphs.iter ()
-  | hb_map_retains_sorting ([&] (const HBGlyphID16& gid) -> hb_pair_t<hb_codepoint_t, unsigned>
-			    {
-			      unsigned new_klass = klass_map->get (gid_klass_map[gid]);
-			      return hb_pair ((hb_codepoint_t)gid, new_klass);
-			    })
-  ;
 
-  c->propagate_error (glyphs, klasses);
-  ClassDef_serialize (c, it);
+  for (unsigned i = 0; i < glyph_and_klass.length; i++)
+  {
+    hb_codepoint_t klass = glyph_and_klass[i].second;
+    glyph_and_klass[i].second = klass_map->get (klass);
+  }
+
+  c->propagate_error (glyph_and_klass, klasses);
+  ClassDef_serialize (c, glyph_and_klass.iter ());
 }
 
 /*
@@ -1949,36 +1962,37 @@ struct ClassDefFormat1
                const Coverage* glyph_filter = nullptr) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    const hb_map_t &glyph_map = *c->plan->glyph_map_gsub;
 
-    hb_sorted_vector_t<HBGlyphID16> glyphs;
+    hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
     hb_set_t orig_klasses;
-    hb_map_t gid_org_klass_map;
 
     hb_codepoint_t start = startGlyph;
     hb_codepoint_t end   = start + classValue.len;
 
-    for (const hb_codepoint_t gid : + hb_range (start, end)
-                                    | hb_filter (glyphset))
+    for (const hb_codepoint_t gid : + hb_range (start, end))
     {
+      hb_codepoint_t new_gid = glyph_map[gid];
+      if (new_gid == HB_MAP_VALUE_INVALID) continue;
       if (glyph_filter && !glyph_filter->has(gid)) continue;
 
       unsigned klass = classValue[gid - start];
       if (!klass) continue;
 
-      glyphs.push (glyph_map[gid]);
-      gid_org_klass_map.set (glyph_map[gid], klass);
+      glyph_and_klass.push (hb_pair (new_gid, klass));
       orig_klasses.add (klass);
     }
 
     unsigned glyph_count = glyph_filter
-                           ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter))
-                           : glyphset.get_population ();
-    use_class_zero = use_class_zero && glyph_count <= gid_org_klass_map.get_population ();
-    ClassDef_remap_and_serialize (c->serializer, gid_org_klass_map,
-				  glyphs, orig_klasses, use_class_zero, klass_map);
-    return_trace (keep_empty_table || (bool) glyphs);
+                           ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter))
+                           : glyph_map.get_population ();
+    use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length;
+    ClassDef_remap_and_serialize (c->serializer,
+                                  orig_klasses,
+                                  use_class_zero,
+                                  glyph_and_klass,
+                                  klass_map);
+    return_trace (keep_empty_table || (bool) glyph_and_klass);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -1987,6 +2001,8 @@ struct ClassDefFormat1
     return_trace (c->check_struct (this) && classValue.sanitize (c));
   }
 
+  unsigned cost () const { return 1; }
+
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
   {
@@ -2044,10 +2060,9 @@ struct ClassDefFormat1
     }
     /* TODO Speed up, using set overlap first? */
     /* TODO(iter) Rewrite as dagger. */
-    HBUINT16 k {klass};
     const HBUINT16 *arr = classValue.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (arr[i] == k && glyphs->has (startGlyph + i))
+      if (arr[i] == klass && glyphs->has (startGlyph + i))
 	return true;
     return false;
   }
@@ -2057,17 +2072,32 @@ struct ClassDefFormat1
     unsigned count = classValue.len;
     if (klass == 0)
     {
-      hb_codepoint_t endGlyph = startGlyph + count -1;
-      for (hb_codepoint_t g : glyphs->iter ())
-        if (g < startGlyph || g > endGlyph)
-          intersect_glyphs->add (g);
+      unsigned start_glyph = startGlyph;
+      for (unsigned g = HB_SET_VALUE_INVALID;
+	   hb_set_next (glyphs, &g) && g < start_glyph;)
+	intersect_glyphs->add (g);
+
+      for (unsigned g = startGlyph + count - 1;
+	   hb_set_next (glyphs, &g);)
+	intersect_glyphs->add (g);
 
       return;
     }
 
     for (unsigned i = 0; i < count; i++)
       if (classValue[i] == klass && glyphs->has (startGlyph + i))
-        intersect_glyphs->add (startGlyph + i);
+	intersect_glyphs->add (startGlyph + i);
+
+#if 0
+    /* The following implementation is faster asymptotically, but slower
+     * in practice. */
+    unsigned start_glyph = startGlyph;
+    unsigned end_glyph = start_glyph + count;
+    for (unsigned g = startGlyph - 1;
+	 hb_set_next (glyphs, &g) && g < end_glyph;)
+      if (classValue.arrayZ[g - start_glyph] == klass)
+        intersect_glyphs->add (g);
+#endif
   }
 
   void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const
@@ -2167,12 +2197,10 @@ struct ClassDefFormat2
                const Coverage* glyph_filter = nullptr) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    const hb_map_t &glyph_map = *c->plan->glyph_map_gsub;
 
-    hb_sorted_vector_t<HBGlyphID16> glyphs;
+    hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
     hb_set_t orig_klasses;
-    hb_map_t gid_org_klass_map;
 
     unsigned count = rangeRecord.len;
     for (unsigned i = 0; i < count; i++)
@@ -2183,21 +2211,26 @@ struct ClassDefFormat2
       hb_codepoint_t end   = rangeRecord[i].last + 1;
       for (hb_codepoint_t g = start; g < end; g++)
       {
-	if (!glyphset.has (g)) continue;
+        hb_codepoint_t new_gid = glyph_map[g];
+	if (new_gid == HB_MAP_VALUE_INVALID) continue;
         if (glyph_filter && !glyph_filter->has (g)) continue;
-	glyphs.push (glyph_map[g]);
-	gid_org_klass_map.set (glyph_map[g], klass);
+
+	glyph_and_klass.push (hb_pair (new_gid, klass));
 	orig_klasses.add (klass);
       }
     }
 
+    const hb_set_t& glyphset = *c->plan->glyphset_gsub ();
     unsigned glyph_count = glyph_filter
                            ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter))
-                           : glyphset.get_population ();
-    use_class_zero = use_class_zero && glyph_count <= gid_org_klass_map.get_population ();
-    ClassDef_remap_and_serialize (c->serializer, gid_org_klass_map,
-				  glyphs, orig_klasses, use_class_zero, klass_map);
-    return_trace (keep_empty_table || (bool) glyphs);
+                           : glyph_map.get_population ();
+    use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length;
+    ClassDef_remap_and_serialize (c->serializer,
+                                  orig_klasses,
+                                  use_class_zero,
+                                  glyph_and_klass,
+                                  klass_map);
+    return_trace (keep_empty_table || (bool) glyph_and_klass);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2206,6 +2239,8 @@ struct ClassDefFormat2
     return_trace (rangeRecord.sanitize (c));
   }
 
+  unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ }
+
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
   {
@@ -2263,10 +2298,9 @@ struct ClassDefFormat2
     }
     /* TODO Speed up, using set overlap first? */
     /* TODO(iter) Rewrite as dagger. */
-    HBUINT16 k {klass};
     const RangeRecord *arr = rangeRecord.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (arr[i].value == k && arr[i].intersects (glyphs))
+      if (arr[i].value == klass && arr[i].intersects (glyphs))
 	return true;
     return false;
   }
@@ -2279,43 +2313,44 @@ struct ClassDefFormat2
       hb_codepoint_t g = HB_SET_VALUE_INVALID;
       for (unsigned int i = 0; i < count; i++)
       {
-        if (!hb_set_next (glyphs, &g))
-          break;
-        while (g != HB_SET_VALUE_INVALID && g < rangeRecord[i].first)
-        {
-          intersect_glyphs->add (g);
-          hb_set_next (glyphs, &g);
+	if (!hb_set_next (glyphs, &g))
+	  goto done;
+	while (g < rangeRecord[i].first)
+	{
+	  intersect_glyphs->add (g);
+	  if (!hb_set_next (glyphs, &g))
+	    goto done;
         }
         g = rangeRecord[i].last;
       }
-      while (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
-        intersect_glyphs->add (g);
+      while (hb_set_next (glyphs, &g))
+	intersect_glyphs->add (g);
+      done:
 
       return;
     }
 
-    hb_codepoint_t g = HB_SET_VALUE_INVALID;
+#if 0
+    /* The following implementation is faster asymptotically, but slower
+     * in practice. */
+    if ((count >> 3) > glyphs->get_population ())
+    {
+      for (hb_codepoint_t g = HB_SET_VALUE_INVALID;
+	   hb_set_next (glyphs, &g);)
+        if (rangeRecord.as_array ().bfind (g))
+	  intersect_glyphs->add (g);
+      return;
+    }
+#endif
+
     for (unsigned int i = 0; i < count; i++)
     {
       if (rangeRecord[i].value != klass) continue;
 
-      if (g != HB_SET_VALUE_INVALID)
-      {
-        if (g >= rangeRecord[i].first &&
-            g <= rangeRecord[i].last)
-          intersect_glyphs->add (g);
-        if (g > rangeRecord[i].last)
-          continue;
-      }
-
-      g = rangeRecord[i].first - 1;
-      while (hb_set_next (glyphs, &g))
-      {
-        if (g >= rangeRecord[i].first && g <= rangeRecord[i].last)
-          intersect_glyphs->add (g);
-        else if (g > rangeRecord[i].last)
-          break;
-      }
+      unsigned end = rangeRecord[i].last + 1;
+      for (hb_codepoint_t g = rangeRecord[i].first - 1;
+	   hb_set_next (glyphs, &g) && g < end;)
+	intersect_glyphs->add (g);
     }
   }
 
@@ -2446,6 +2481,15 @@ struct ClassDef
     }
   }
 
+  unsigned cost () const
+  {
+    switch (u.format) {
+    case 1: return u.format1.cost ();
+    case 2: return u.format2.cost ();
+    default:return 0u;
+    }
+  }
+
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
   template <typename set_t>
@@ -2567,14 +2611,27 @@ struct VarRegionAxis
   DEFINE_SIZE_STATIC (6);
 };
 
+#define REGION_CACHE_ITEM_CACHE_INVALID 2.f
+
 struct VarRegionList
 {
+  using cache_t = float;
+
   float evaluate (unsigned int region_index,
-		  const int *coords, unsigned int coord_len) const
+		  const int *coords, unsigned int coord_len,
+		  cache_t *cache = nullptr) const
   {
     if (unlikely (region_index >= regionCount))
       return 0.;
 
+    float *cached_value = nullptr;
+    if (cache)
+    {
+      cached_value = &(cache[region_index]);
+      if (likely (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID))
+	return *cached_value;
+    }
+
     const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount);
 
     float v = 1.;
@@ -2584,9 +2641,16 @@ struct VarRegionList
       int coord = i < coord_len ? coords[i] : 0;
       float factor = axes[i].evaluate (coord);
       if (factor == 0.f)
+      {
+        if (cache)
+	  *cached_value = 0.;
 	return 0.;
+      }
       v *= factor;
     }
+
+    if (cache)
+      *cached_value = v;
     return v;
   }
 
@@ -2634,7 +2698,7 @@ struct VarData
   { return regionIndices.len; }
 
   unsigned int get_row_size () const
-  { return shortCount + regionIndices.len; }
+  { return (wordCount () + regionIndices.len) * (longWords () ? 2 : 1); }
 
   unsigned int get_size () const
   { return min_size
@@ -2644,13 +2708,17 @@ struct VarData
 
   float get_delta (unsigned int inner,
 		   const int *coords, unsigned int coord_count,
-		   const VarRegionList &regions) const
+		   const VarRegionList &regions,
+		   VarRegionList::cache_t *cache = nullptr) const
   {
     if (unlikely (inner >= itemCount))
       return 0.;
 
    unsigned int count = regionIndices.len;
-   unsigned int scount = shortCount;
+   bool is_long = longWords ();
+   unsigned word_count = wordCount ();
+   unsigned int scount = is_long ? count - word_count : word_count;
+   unsigned int lcount = is_long ? word_count : 0;
 
    const HBUINT8 *bytes = get_delta_bytes ();
    const HBUINT8 *row = bytes + inner * (scount + count);
@@ -2658,16 +2726,22 @@ struct VarData
    float delta = 0.;
    unsigned int i = 0;
 
-   const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (row);
+   const HBINT16 *lcursor = reinterpret_cast<const HBINT16 *> (row);
+   for (; i < lcount; i++)
+   {
+     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
+     delta += scalar * *lcursor++;
+   }
+   const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (lcursor);
    for (; i < scount; i++)
    {
-     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count);
+     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
      delta += scalar * *scursor++;
    }
    const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor);
    for (; i < count; i++)
    {
-     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count);
+     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
      delta += scalar * *bcursor++;
    }
 
@@ -2691,7 +2765,7 @@ struct VarData
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  regionIndices.sanitize (c) &&
-		  shortCount <= regionIndices.len &&
+		  wordCount () <= regionIndices.len &&
 		  c->check_range (get_delta_bytes (),
 				  itemCount,
 				  get_row_size ()));
@@ -2706,43 +2780,66 @@ struct VarData
     if (unlikely (!c->extend_min (this))) return_trace (false);
     itemCount = inner_map.get_next_value ();
 
-    /* Optimize short count */
-    unsigned short ri_count = src->regionIndices.len;
-    enum delta_size_t { kZero=0, kByte, kShort };
+    /* Optimize word count */
+    unsigned ri_count = src->regionIndices.len;
+    enum delta_size_t { kZero=0, kNonWord, kWord };
     hb_vector_t<delta_size_t> delta_sz;
     hb_vector_t<unsigned int> ri_map;	/* maps old index to new index */
     delta_sz.resize (ri_count);
     ri_map.resize (ri_count);
-    unsigned int new_short_count = 0;
+    unsigned int new_word_count = 0;
     unsigned int r;
+
+    bool has_long = false;
+    if (src->longWords ())
+    {
+      for (r = 0; r < ri_count; r++)
+      {
+	for (unsigned int i = 0; i < inner_map.get_next_value (); i++)
+	{
+	  unsigned int old = inner_map.backward (i);
+	  int32_t delta = src->get_item_delta (old, r);
+	  if (delta < -65536 || 65535 < delta)
+	  {
+	    has_long = true;
+	    break;
+	  }
+        }
+      }
+    }
+
+    signed min_threshold = has_long ? -65536 : -128;
+    signed max_threshold = has_long ? +65535 : +127;
     for (r = 0; r < ri_count; r++)
     {
       delta_sz[r] = kZero;
       for (unsigned int i = 0; i < inner_map.get_next_value (); i++)
       {
 	unsigned int old = inner_map.backward (i);
-	int16_t delta = src->get_item_delta (old, r);
-	if (delta < -128 || 127 < delta)
+	int32_t delta = src->get_item_delta (old, r);
+	if (delta < min_threshold || max_threshold < delta)
 	{
-	  delta_sz[r] = kShort;
-	  new_short_count++;
+	  delta_sz[r] = kWord;
+	  new_word_count++;
 	  break;
 	}
 	else if (delta != 0)
-	  delta_sz[r] = kByte;
+	  delta_sz[r] = kNonWord;
       }
     }
-    unsigned int short_index = 0;
-    unsigned int byte_index = new_short_count;
+
+    unsigned int word_index = 0;
+    unsigned int non_word_index = new_word_count;
     unsigned int new_ri_count = 0;
     for (r = 0; r < ri_count; r++)
       if (delta_sz[r])
       {
-	ri_map[r] = (delta_sz[r] == kShort)? short_index++ : byte_index++;
+	ri_map[r] = (delta_sz[r] == kWord)? word_index++ : non_word_index++;
 	new_ri_count++;
       }
 
-    shortCount = new_short_count;
+    wordSizeCount = new_word_count | (has_long ? 0x8000u /* LONG_WORDS */ : 0);
+
     regionIndices.len = new_ri_count;
 
     if (unlikely (!c->extend (this))) return_trace (false);
@@ -2782,28 +2879,55 @@ struct VarData
   HBUINT8 *get_delta_bytes ()
   { return &StructAfter<HBUINT8> (regionIndices); }
 
-  int16_t get_item_delta (unsigned int item, unsigned int region) const
+  int32_t get_item_delta (unsigned int item, unsigned int region) const
   {
     if ( item >= itemCount || unlikely (region >= regionIndices.len)) return 0;
-    const HBINT8 *p = (const HBINT8 *)get_delta_bytes () + item * get_row_size ();
-    if (region < shortCount)
-      return ((const HBINT16 *)p)[region];
+    const HBINT8 *p = (const HBINT8 *) get_delta_bytes () + item * get_row_size ();
+    unsigned word_count = wordCount ();
+    bool is_long = longWords ();
+    if (is_long)
+    {
+      if (region < word_count)
+	return ((const HBINT32 *) p)[region];
+      else
+	return ((const HBINT16 *)(p + HBINT32::static_size * word_count))[region - word_count];
+    }
     else
-      return (p + HBINT16::static_size * shortCount)[region - shortCount];
+    {
+      if (region < word_count)
+	return ((const HBINT16 *) p)[region];
+      else
+	return (p + HBINT16::static_size * word_count)[region - word_count];
+    }
   }
 
-  void set_item_delta (unsigned int item, unsigned int region, int16_t delta)
+  void set_item_delta (unsigned int item, unsigned int region, int32_t delta)
   {
     HBINT8 *p = (HBINT8 *)get_delta_bytes () + item * get_row_size ();
-    if (region < shortCount)
-      ((HBINT16 *)p)[region] = delta;
+    unsigned word_count = wordCount ();
+    bool is_long = longWords ();
+    if (is_long)
+    {
+      if (region < word_count)
+	((HBINT32 *) p)[region] = delta;
+      else
+	((HBINT16 *)(p + HBINT32::static_size * word_count))[region - word_count] = delta;
+    }
     else
-      (p + HBINT16::static_size * shortCount)[region - shortCount] = delta;
+    {
+      if (region < word_count)
+	((HBINT16 *) p)[region] = delta;
+      else
+	(p + HBINT16::static_size * word_count)[region - word_count] = delta;
+    }
   }
 
+  bool longWords () const { return wordSizeCount & 0x8000u /* LONG_WORDS */; }
+  unsigned wordCount () const { return wordSizeCount & 0x7FFFu /* WORD_DELTA_COUNT_MASK */; }
+
   protected:
   HBUINT16		itemCount;
-  HBUINT16		shortCount;
+  HBUINT16		wordSizeCount;
   Array16Of<HBUINT16>	regionIndices;
 /*UnsizedArrayOf<HBUINT8>bytesX;*/
   public:
@@ -2812,9 +2936,28 @@ struct VarData
 
 struct VariationStore
 {
+  using cache_t = VarRegionList::cache_t;
+
+  cache_t *create_cache () const
+  {
+    auto &r = this+regions;
+    unsigned count = r.regionCount;
+
+    float *cache = (float *) hb_malloc (sizeof (float) * count);
+    if (unlikely (!cache)) return nullptr;
+
+    for (unsigned i = 0; i < count; i++)
+      cache[i] = REGION_CACHE_ITEM_CACHE_INVALID;
+
+    return cache;
+  }
+
+  static void destroy_cache (cache_t *cache) { hb_free (cache); }
+
   private:
   float get_delta (unsigned int outer, unsigned int inner,
-		   const int *coords, unsigned int coord_count) const
+		   const int *coords, unsigned int coord_count,
+		   VarRegionList::cache_t *cache = nullptr) const
   {
 #ifdef HB_NO_VAR
     return 0.f;
@@ -2825,16 +2968,18 @@ struct VariationStore
 
     return (this+dataSets[outer]).get_delta (inner,
 					     coords, coord_count,
-					     this+regions);
+					     this+regions,
+					     cache);
   }
 
   public:
   float get_delta (unsigned int index,
-		   const int *coords, unsigned int coord_count) const
+		   const int *coords, unsigned int coord_count,
+		   VarRegionList::cache_t *cache = nullptr) const
   {
     unsigned int outer = index >> 16;
     unsigned int inner = index & 0xFFFF;
-    return get_delta (outer, inner, coords, coord_count);
+    return get_delta (outer, inner, coords, coord_count, cache);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2961,6 +3106,8 @@ struct VariationStore
   DEFINE_SIZE_ARRAY_SIZED (8, dataSets);
 };
 
+#undef REGION_CACHE_ITEM_CACHE_INVALID
+
 /*
  * Feature Variations
  */
@@ -3428,11 +3575,15 @@ struct VariationDevice
 
   private:
 
-  hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store) const
-  { return font->em_scalef_x (get_delta (font, store)); }
+  hb_position_t get_x_delta (hb_font_t *font,
+			     const VariationStore &store,
+			     VariationStore::cache_t *store_cache = nullptr) const
+  { return font->em_scalef_x (get_delta (font, store, store_cache)); }
 
-  hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
-  { return font->em_scalef_y (get_delta (font, store)); }
+  hb_position_t get_y_delta (hb_font_t *font,
+			     const VariationStore &store,
+			     VariationStore::cache_t *store_cache = nullptr) const
+  { return font->em_scalef_y (get_delta (font, store, store_cache)); }
 
   VariationDevice* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const
   {
@@ -3466,9 +3617,11 @@ struct VariationDevice
 
   private:
 
-  float get_delta (hb_font_t *font, const VariationStore &store) const
+  float get_delta (hb_font_t *font,
+		   const VariationStore &store,
+		   VariationStore::cache_t *store_cache = nullptr) const
   {
-    return store.get_delta (varIdx, font->coords, font->num_coords);
+    return store.get_delta (varIdx, font->coords, font->num_coords, (VariationStore::cache_t *) store_cache);
   }
 
   protected:
@@ -3491,7 +3644,9 @@ struct DeviceHeader
 
 struct Device
 {
-  hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store=Null (VariationStore)) const
+  hb_position_t get_x_delta (hb_font_t *font,
+			     const VariationStore &store=Null (VariationStore),
+			     VariationStore::cache_t *store_cache = nullptr) const
   {
     switch (u.b.format)
     {
@@ -3501,13 +3656,15 @@ struct Device
 #endif
 #ifndef HB_NO_VAR
     case 0x8000:
-      return u.variation.get_x_delta (font, store);
+      return u.variation.get_x_delta (font, store, store_cache);
 #endif
     default:
       return 0;
     }
   }
-  hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store=Null (VariationStore)) const
+  hb_position_t get_y_delta (hb_font_t *font,
+			     const VariationStore &store=Null (VariationStore),
+			     VariationStore::cache_t *store_cache = nullptr) const
   {
     switch (u.b.format)
     {
@@ -3517,7 +3674,7 @@ struct Device
 #endif
 #ifndef HB_NO_VAR
     case 0x8000:
-      return u.variation.get_y_delta (font, store);
+      return u.variation.get_y_delta (font, store, store_cache);
 #endif
     default:
       return 0;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gdef-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gdef-table.hh
index a76d644c4b226f39ff24476951002236cf83f509..9d47bac2b9b866d76a521e51ac7f87032ca7d8a4 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gdef-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gdef-table.hh
@@ -571,7 +571,7 @@ struct GDEF
     static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks), "");
 
     switch (klass) {
-    default:			return 0;
+    default:			return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
     case BaseGlyph:		return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH;
     case LigatureGlyph:		return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
     case MarkGlyph:
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gpos-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gpos-table.hh
index 2f9186a2a75da8cc9c34d90d990886793f4e0b49..59795cbba3ef4631a72c0af28be0b0d0cf826cb6 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gpos-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gpos-table.hh
@@ -29,3090 +29,17 @@
 #ifndef HB_OT_LAYOUT_GPOS_TABLE_HH
 #define HB_OT_LAYOUT_GPOS_TABLE_HH
 
-#include "hb-ot-layout-gsubgpos.hh"
-
-
-namespace OT {
-
-struct MarkArray;
-static void Markclass_closure_and_remap_indexes (const Coverage  &mark_coverage,
-						 const MarkArray &mark_array,
-						 const hb_set_t  &glyphset,
-						 hb_map_t*        klass_mapping /* INOUT */);
-
-/* buffer **position** var allocations */
-#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */
-#define attach_type() var.u8[2] /* attachment type */
-/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */
-
-enum attach_type_t {
-  ATTACH_TYPE_NONE	= 0X00,
-
-  /* Each attachment should be either a mark or a cursive; can't be both. */
-  ATTACH_TYPE_MARK	= 0X01,
-  ATTACH_TYPE_CURSIVE	= 0X02,
-};
-
-
-/* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
-
-typedef HBUINT16 Value;
-
-typedef UnsizedArrayOf<Value> ValueRecord;
-
-struct ValueFormat : HBUINT16
-{
-  enum Flags {
-    xPlacement	= 0x0001u,	/* Includes horizontal adjustment for placement */
-    yPlacement	= 0x0002u,	/* Includes vertical adjustment for placement */
-    xAdvance	= 0x0004u,	/* Includes horizontal adjustment for advance */
-    yAdvance	= 0x0008u,	/* Includes vertical adjustment for advance */
-    xPlaDevice	= 0x0010u,	/* Includes horizontal Device table for placement */
-    yPlaDevice	= 0x0020u,	/* Includes vertical Device table for placement */
-    xAdvDevice	= 0x0040u,	/* Includes horizontal Device table for advance */
-    yAdvDevice	= 0x0080u,	/* Includes vertical Device table for advance */
-    ignored	= 0x0F00u,	/* Was used in TrueType Open for MM fonts */
-    reserved	= 0xF000u,	/* For future use */
-
-    devices	= 0x00F0u	/* Mask for having any Device table */
-  };
-
-/* All fields are options.  Only those available advance the value pointer. */
-#if 0
-  HBINT16		xPlacement;	/* Horizontal adjustment for
-					 * placement--in design units */
-  HBINT16		yPlacement;	/* Vertical adjustment for
-					 * placement--in design units */
-  HBINT16		xAdvance;	/* Horizontal adjustment for
-					 * advance--in design units (only used
-					 * for horizontal writing) */
-  HBINT16		yAdvance;	/* Vertical adjustment for advance--in
-					 * design units (only used for vertical
-					 * writing) */
-  Offset16To<Device>	xPlaDevice;	/* Offset to Device table for
-					 * horizontal placement--measured from
-					 * beginning of PosTable (may be NULL) */
-  Offset16To<Device>	yPlaDevice;	/* Offset to Device table for vertical
-					 * placement--measured from beginning
-					 * of PosTable (may be NULL) */
-  Offset16To<Device>	xAdvDevice;	/* Offset to Device table for
-					 * horizontal advance--measured from
-					 * beginning of PosTable (may be NULL) */
-  Offset16To<Device>	yAdvDevice;	/* Offset to Device table for vertical
-					 * advance--measured from beginning of
-					 * PosTable (may be NULL) */
-#endif
-
-  IntType& operator = (uint16_t i) { v = i; return *this; }
-
-  unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
-  unsigned int get_size () const { return get_len () * Value::static_size; }
-
-  bool apply_value (hb_ot_apply_context_t *c,
-		    const void            *base,
-		    const Value           *values,
-		    hb_glyph_position_t   &glyph_pos) const
-  {
-    bool ret = false;
-    unsigned int format = *this;
-    if (!format) return ret;
-
-    hb_font_t *font = c->font;
-    bool horizontal =
-#ifndef HB_NO_VERTICAL
-      HB_DIRECTION_IS_HORIZONTAL (c->direction)
-#else
-      true
-#endif
-      ;
-
-    if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
-    if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
-    if (format & xAdvance) {
-      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
-      values++;
-    }
-    /* y_advance values grow downward but font-space grows upward, hence negation */
-    if (format & yAdvance) {
-      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
-      values++;
-    }
-
-    if (!has_device ()) return ret;
-
-    bool use_x_device = font->x_ppem || font->num_coords;
-    bool use_y_device = font->y_ppem || font->num_coords;
-
-    if (!use_x_device && !use_y_device) return ret;
-
-    const VariationStore &store = c->var_store;
-
-    /* pixel -> fractional pixel */
-    if (format & xPlaDevice) {
-      if (use_x_device) glyph_pos.x_offset  += (base + get_device (values, &ret)).get_x_delta (font, store);
-      values++;
-    }
-    if (format & yPlaDevice) {
-      if (use_y_device) glyph_pos.y_offset  += (base + get_device (values, &ret)).get_y_delta (font, store);
-      values++;
-    }
-    if (format & xAdvDevice) {
-      if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store);
-      values++;
-    }
-    if (format & yAdvDevice) {
-      /* y_advance values grow downward but font-space grows upward, hence negation */
-      if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store);
-      values++;
-    }
-    return ret;
-  }
-
-  unsigned int get_effective_format (const Value *values) const
-  {
-    unsigned int format = *this;
-    for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
-      if (format & flag) should_drop (*values++, (Flags) flag, &format);
-    }
-
-    return format;
-  }
-
-  template<typename Iterator,
-      hb_requires (hb_is_iterator (Iterator))>
-  unsigned int get_effective_format (Iterator it) const {
-    unsigned int new_format = 0;
-
-    for (const hb_array_t<const Value>& values : it)
-      new_format = new_format | get_effective_format (&values);
-
-    return new_format;
-  }
-
-  void copy_values (hb_serialize_context_t *c,
-                    unsigned int new_format,
-                    const void *base,
-                    const Value *values,
-                    const hb_map_t *layout_variation_idx_map) const
-  {
-    unsigned int format = *this;
-    if (!format) return;
-
-    if (format & xPlacement) copy_value (c, new_format, xPlacement, *values++);
-    if (format & yPlacement) copy_value (c, new_format, yPlacement, *values++);
-    if (format & xAdvance)   copy_value (c, new_format, xAdvance, *values++);
-    if (format & yAdvance)   copy_value (c, new_format, yAdvance, *values++);
-
-    if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
-    if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
-    if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
-    if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
-  }
-
-  void copy_value (hb_serialize_context_t *c,
-                   unsigned int new_format,
-                   Flags flag,
-                   Value value) const
-  {
-    // Filter by new format.
-    if (!(new_format & flag)) return;
-    c->copy (value);
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-				  const void *base,
-				  const hb_array_t<const Value>& values) const
-  {
-    unsigned format = *this;
-    unsigned i = 0;
-    if (format & xPlacement) i++;
-    if (format & yPlacement) i++;
-    if (format & xAdvance) i++;
-    if (format & yAdvance) i++;
-    if (format & xPlaDevice)
-    {
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
-      i++;
-    }
-
-    if (format & ValueFormat::yPlaDevice)
-    {
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
-      i++;
-    }
-
-    if (format & ValueFormat::xAdvDevice)
-    {
-
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
-      i++;
-    }
-
-    if (format & ValueFormat::yAdvDevice)
-    {
-
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
-      i++;
-    }
-  }
-
-  private:
-  bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
-  {
-    unsigned int format = *this;
-
-    if (format & xPlacement) values++;
-    if (format & yPlacement) values++;
-    if (format & xAdvance)   values++;
-    if (format & yAdvance)   values++;
-
-    if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
-    if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
-    if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
-    if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
-
-    return true;
-  }
-
-  static inline Offset16To<Device>& get_device (Value* value)
-  {
-    return *static_cast<Offset16To<Device> *> (value);
-  }
-  static inline const Offset16To<Device>& get_device (const Value* value, bool *worked=nullptr)
-  {
-    if (worked) *worked |= bool (*value);
-    return *static_cast<const Offset16To<Device> *> (value);
-  }
-
-  bool copy_device (hb_serialize_context_t *c, const void *base,
-		    const Value *src_value, const hb_map_t *layout_variation_idx_map) const
-  {
-    Value	*dst_value = c->copy (*src_value);
-
-    if (!dst_value) return false;
-    if (*dst_value == 0) return true;
-
-    *dst_value = 0;
-    c->push ();
-    if ((base + get_device (src_value)).copy (c, layout_variation_idx_map))
-    {
-      c->add_link (*dst_value, c->pop_pack ());
-      return true;
-    }
-    else
-    {
-      c->pop_discard ();
-      return false;
-    }
-  }
-
-  static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr)
-  {
-    if (worked) *worked |= bool (*value);
-    return *reinterpret_cast<const HBINT16 *> (value);
-  }
-
-  public:
-
-  bool has_device () const
-  {
-    unsigned int format = *this;
-    return (format & devices) != 0;
-  }
-
-  bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
-  }
-
-  bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
-  {
-    TRACE_SANITIZE (this);
-    unsigned int len = get_len ();
-
-    if (!c->check_range (values, count, get_size ())) return_trace (false);
-
-    if (!has_device ()) return_trace (true);
-
-    for (unsigned int i = 0; i < count; i++) {
-      if (!sanitize_value_devices (c, base, values))
-	return_trace (false);
-      values += len;
-    }
-
-    return_trace (true);
-  }
-
-  /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
-  bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
-  {
-    TRACE_SANITIZE (this);
-
-    if (!has_device ()) return_trace (true);
-
-    for (unsigned int i = 0; i < count; i++) {
-      if (!sanitize_value_devices (c, base, values))
-	return_trace (false);
-      values += stride;
-    }
-
-    return_trace (true);
-  }
-
- private:
-
-  void should_drop (Value value, Flags flag, unsigned int* format) const
-  {
-    if (value) return;
-    *format = *format & ~flag;
-  }
-
-};
-
-template<typename Iterator, typename SrcLookup>
-static void SinglePos_serialize (hb_serialize_context_t *c,
-				 const SrcLookup *src,
-				 Iterator it,
-				 const hb_map_t *layout_variation_idx_map);
-
-
-struct AnchorFormat1
-{
-  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
-		   float *x, float *y) const
-  {
-    hb_font_t *font = c->font;
-    *x = font->em_fscale_x (xCoordinate);
-    *y = font->em_fscale_y (yCoordinate);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  AnchorFormat1* copy (hb_serialize_context_t *c) const
-  {
-    TRACE_SERIALIZE (this);
-    AnchorFormat1* out = c->embed<AnchorFormat1> (this);
-    if (!out) return_trace (out);
-    out->format = 1;
-    return_trace (out);
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 1 */
-  FWORD		xCoordinate;		/* Horizontal value--in design units */
-  FWORD		yCoordinate;		/* Vertical value--in design units */
-  public:
-  DEFINE_SIZE_STATIC (6);
-};
-
-struct AnchorFormat2
-{
-  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
-		   float *x, float *y) const
-  {
-    hb_font_t *font = c->font;
-
-#ifdef HB_NO_HINTING
-    *x = font->em_fscale_x (xCoordinate);
-    *y = font->em_fscale_y (yCoordinate);
-    return;
-#endif
-
-    unsigned int x_ppem = font->x_ppem;
-    unsigned int y_ppem = font->y_ppem;
-    hb_position_t cx = 0, cy = 0;
-    bool ret;
-
-    ret = (x_ppem || y_ppem) &&
-	  font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
-    *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate);
-    *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  AnchorFormat2* copy (hb_serialize_context_t *c) const
-  {
-    TRACE_SERIALIZE (this);
-    return_trace (c->embed<AnchorFormat2> (this));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 2 */
-  FWORD		xCoordinate;		/* Horizontal value--in design units */
-  FWORD		yCoordinate;		/* Vertical value--in design units */
-  HBUINT16	anchorPoint;		/* Index to glyph contour point */
-  public:
-  DEFINE_SIZE_STATIC (8);
-};
-
-struct AnchorFormat3
-{
-  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
-		   float *x, float *y) const
-  {
-    hb_font_t *font = c->font;
-    *x = font->em_fscale_x (xCoordinate);
-    *y = font->em_fscale_y (yCoordinate);
-
-    if (font->x_ppem || font->num_coords)
-      *x += (this+xDeviceTable).get_x_delta (font, c->var_store);
-    if (font->y_ppem || font->num_coords)
-      *y += (this+yDeviceTable).get_y_delta (font, c->var_store);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
-  }
-
-  AnchorFormat3* copy (hb_serialize_context_t *c,
-		       const hb_map_t *layout_variation_idx_map) const
-  {
-    TRACE_SERIALIZE (this);
-    if (!layout_variation_idx_map) return_trace (nullptr);
-
-    auto *out = c->embed<AnchorFormat3> (this);
-    if (unlikely (!out)) return_trace (nullptr);
-
-    out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
-    out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
-    return_trace (out);
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices);
-    (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices);
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 3 */
-  FWORD		xCoordinate;		/* Horizontal value--in design units */
-  FWORD		yCoordinate;		/* Vertical value--in design units */
-  Offset16To<Device>
-		xDeviceTable;		/* Offset to Device table for X
-					 * coordinate-- from beginning of
-					 * Anchor table (may be NULL) */
-  Offset16To<Device>
-		yDeviceTable;		/* Offset to Device table for Y
-					 * coordinate-- from beginning of
-					 * Anchor table (may be NULL) */
-  public:
-  DEFINE_SIZE_STATIC (10);
-};
-
-struct Anchor
-{
-  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
-		   float *x, float *y) const
-  {
-    *x = *y = 0;
-    switch (u.format) {
-    case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
-    case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
-    case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
-    default:					      return;
-    }
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return_trace (false);
-    switch (u.format) {
-    case 1: return_trace (u.format1.sanitize (c));
-    case 2: return_trace (u.format2.sanitize (c));
-    case 3: return_trace (u.format3.sanitize (c));
-    default:return_trace (true);
-    }
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    switch (u.format) {
-    case 1: return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer))));
-    case 2:
-      if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
-      {
-        // AnchorFormat 2 just containins extra hinting information, so
-        // if hints are being dropped convert to format 1.
-        return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer))));
-      }
-      return_trace (bool (reinterpret_cast<Anchor *> (u.format2.copy (c->serializer))));
-    case 3: return_trace (bool (reinterpret_cast<Anchor *> (u.format3.copy (c->serializer,
-                                                                            c->plan->layout_variation_idx_map))));
-    default:return_trace (false);
-    }
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    switch (u.format) {
-    case 1: case 2:
-      return;
-    case 3:
-      u.format3.collect_variation_indices (c);
-      return;
-    default: return;
-    }
-  }
-
-  protected:
-  union {
-  HBUINT16		format;		/* Format identifier */
-  AnchorFormat1		format1;
-  AnchorFormat2		format2;
-  AnchorFormat3		format3;
-  } u;
-  public:
-  DEFINE_SIZE_UNION (2, format);
-};
-
-
-struct AnchorMatrix
-{
-  const Anchor& get_anchor (unsigned int row, unsigned int col,
-			    unsigned int cols, bool *found) const
-  {
-    *found = false;
-    if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
-    *found = !matrixZ[row * cols + col].is_null ();
-    return this+matrixZ[row * cols + col];
-  }
-
-  template <typename Iterator,
-	    hb_requires (hb_is_iterator (Iterator))>
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-				  Iterator index_iter) const
-  {
-    for (unsigned i : index_iter)
-      (this+matrixZ[i]).collect_variation_indices (c);
-  }
-
-  template <typename Iterator,
-      hb_requires (hb_is_iterator (Iterator))>
-  bool subset (hb_subset_context_t *c,
-               unsigned             num_rows,
-               Iterator             index_iter) const
-  {
-    TRACE_SUBSET (this);
-
-    auto *out = c->serializer->start_embed (this);
-
-    if (!index_iter) return_trace (false);
-    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
-
-    out->rows = num_rows;
-    for (const unsigned i : index_iter)
-    {
-      auto *offset = c->serializer->embed (matrixZ[i]);
-      if (!offset) return_trace (false);
-      offset->serialize_subset (c, matrixZ[i], this);
-    }
-
-    return_trace (true);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
-  {
-    TRACE_SANITIZE (this);
-    if (!c->check_struct (this)) return_trace (false);
-    if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
-    unsigned int count = rows * cols;
-    if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
-    for (unsigned int i = 0; i < count; i++)
-      if (!matrixZ[i].sanitize (c, this)) return_trace (false);
-    return_trace (true);
-  }
-
-  HBUINT16	rows;			/* Number of rows */
-  UnsizedArrayOf<Offset16To<Anchor>>
-		matrixZ;		/* Matrix of offsets to Anchor tables--
-					 * from beginning of AnchorMatrix table */
-  public:
-  DEFINE_SIZE_ARRAY (2, matrixZ);
-};
-
-
-struct MarkRecord
-{
-  friend struct MarkArray;
-
-  unsigned get_class () const { return (unsigned) klass; }
-  bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && markAnchor.sanitize (c, base));
-  }
-
-  MarkRecord *subset (hb_subset_context_t    *c,
-                      const void             *src_base,
-                      const hb_map_t         *klass_mapping) const
-  {
-    TRACE_SUBSET (this);
-    auto *out = c->serializer->embed (this);
-    if (unlikely (!out)) return_trace (nullptr);
-
-    out->klass = klass_mapping->get (klass);
-    out->markAnchor.serialize_subset (c, markAnchor, src_base);
-    return_trace (out);
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-				  const void *src_base) const
-  {
-    (src_base+markAnchor).collect_variation_indices (c);
-  }
-
-  protected:
-  HBUINT16	klass;			/* Class defined for this mark */
-  Offset16To<Anchor>
-		markAnchor;		/* Offset to Anchor table--from
-					 * beginning of MarkArray table */
-  public:
-  DEFINE_SIZE_STATIC (4);
-};
-
-struct MarkArray : Array16Of<MarkRecord>	/* Array of MarkRecords--in Coverage order */
-{
-  bool apply (hb_ot_apply_context_t *c,
-	      unsigned int mark_index, unsigned int glyph_index,
-	      const AnchorMatrix &anchors, unsigned int class_count,
-	      unsigned int glyph_pos) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    const MarkRecord &record = Array16Of<MarkRecord>::operator[](mark_index);
-    unsigned int mark_class = record.klass;
-
-    const Anchor& mark_anchor = this + record.markAnchor;
-    bool found;
-    const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
-    /* If this subtable doesn't have an anchor for this base and this class,
-     * return false such that the subsequent subtables have a chance at it. */
-    if (unlikely (!found)) return_trace (false);
-
-    float mark_x, mark_y, base_x, base_y;
-
-    buffer->unsafe_to_break (glyph_pos, buffer->idx + 1);
-    mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
-    glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
-
-    hb_glyph_position_t &o = buffer->cur_pos();
-    o.x_offset = roundf (base_x - mark_x);
-    o.y_offset = roundf (base_y - mark_y);
-    o.attach_type() = ATTACH_TYPE_MARK;
-    o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
-    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
-
-    buffer->idx++;
-    return_trace (true);
-  }
-
-  template <typename Iterator,
-      hb_requires (hb_is_iterator (Iterator))>
-  bool subset (hb_subset_context_t *c,
-               Iterator		    coverage,
-               const hb_map_t      *klass_mapping) const
-  {
-    TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-
-    auto* out = c->serializer->start_embed (this);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-
-    auto mark_iter =
-    + hb_zip (coverage, this->iter ())
-    | hb_filter (glyphset, hb_first)
-    | hb_map (hb_second)
-    ;
-
-    unsigned new_length = 0;
-    for (const auto& mark_record : mark_iter) {
-      if (unlikely (!mark_record.subset (c, this, klass_mapping)))
-        return_trace (false);
-      new_length++;
-    }
-
-    if (unlikely (!c->serializer->check_assign (out->len, new_length,
-                                                HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)))
-      return_trace (false);
-
-    return_trace (true);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (Array16Of<MarkRecord>::sanitize (c, this));
-  }
-};
-
-
-/* Lookups */
-
-struct SinglePosFormat1
-{
-  bool intersects (const hb_set_t *glyphs) const
-  { return (this+coverage).intersects (glyphs); }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    if (!valueFormat.has_device ()) return;
-
-    auto it =
-    + hb_iter (this+coverage)
-    | hb_filter (c->glyph_set)
-    ;
-
-    if (!it) return;
-    valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ()));
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
-
-  const Coverage &get_coverage () const { return this+coverage; }
-
-  ValueFormat get_value_format () const { return valueFormat; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
-
-    valueFormat.apply_value (c, this, values, buffer->cur_pos());
-
-    buffer->idx++;
-    return_trace (true);
-  }
-
-  template<typename Iterator,
-      typename SrcLookup,
-      hb_requires (hb_is_iterator (Iterator))>
-  void serialize (hb_serialize_context_t *c,
-		  const SrcLookup *src,
-		  Iterator it,
-		  ValueFormat newFormat,
-		  const hb_map_t *layout_variation_idx_map)
-  {
-    if (unlikely (!c->extend_min (this))) return;
-    if (unlikely (!c->check_assign (valueFormat,
-                                    newFormat,
-                                    HB_SERIALIZE_ERROR_INT_OVERFLOW))) return;
-
-    for (const hb_array_t<const Value>& _ : + it | hb_map (hb_second))
-    {
-      src->get_value_format ().copy_values (c, newFormat, src,  &_, layout_variation_idx_map);
-      // Only serialize the first entry in the iterator, the rest are assumed to
-      // be the same.
-      break;
-    }
-
-    auto glyphs =
-    + it
-    | hb_map_retains_sorting (hb_first)
-    ;
-
-    coverage.serialize_serialize (c, glyphs);
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
-
-    auto it =
-    + hb_iter (this+coverage)
-    | hb_filter (glyphset)
-    | hb_map_retains_sorting (glyph_map)
-    | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
-    ;
-
-    bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
-    return_trace (ret);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  coverage.sanitize (c, this) &&
-		  valueFormat.sanitize_value (c, this, values));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 1 */
-  Offset16To<Coverage>
-		coverage;		/* Offset to Coverage table--from
-					 * beginning of subtable */
-  ValueFormat	valueFormat;		/* Defines the types of data in the
-					 * ValueRecord */
-  ValueRecord	values;			/* Defines positioning
-					 * value(s)--applied to all glyphs in
-					 * the Coverage table */
-  public:
-  DEFINE_SIZE_ARRAY (6, values);
-};
-
-struct SinglePosFormat2
-{
-  bool intersects (const hb_set_t *glyphs) const
-  { return (this+coverage).intersects (glyphs); }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    if (!valueFormat.has_device ()) return;
-
-    auto it =
-    + hb_zip (this+coverage, hb_range ((unsigned) valueCount))
-    | hb_filter (c->glyph_set, hb_first)
-    ;
-
-    if (!it) return;
-
-    unsigned sub_length = valueFormat.get_len ();
-    const hb_array_t<const Value> values_array = values.as_array (valueCount * sub_length);
-
-    for (unsigned i : + it
-		      | hb_map (hb_second))
-      valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length));
-
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
-
-  const Coverage &get_coverage () const { return this+coverage; }
-
-  ValueFormat get_value_format () const { return valueFormat; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
-
-    if (likely (index >= valueCount)) return_trace (false);
-
-    valueFormat.apply_value (c, this,
-			     &values[index * valueFormat.get_len ()],
-			     buffer->cur_pos());
-
-    buffer->idx++;
-    return_trace (true);
-  }
-
-  template<typename Iterator,
-      typename SrcLookup,
-      hb_requires (hb_is_iterator (Iterator))>
-  void serialize (hb_serialize_context_t *c,
-		  const SrcLookup *src,
-		  Iterator it,
-		  ValueFormat newFormat,
-		  const hb_map_t *layout_variation_idx_map)
-  {
-    auto out = c->extend_min (this);
-    if (unlikely (!out)) return;
-    if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return;
-    if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return;
-
-    + it
-    | hb_map (hb_second)
-    | hb_apply ([&] (hb_array_t<const Value> _)
-    { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); })
-    ;
-
-    auto glyphs =
-    + it
-    | hb_map_retains_sorting (hb_first)
-    ;
-
-    coverage.serialize_serialize (c, glyphs);
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    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 ();
-    auto values_array = values.as_array (valueCount * sub_length);
-
-    auto it =
-    + hb_zip (this+coverage, hb_range ((unsigned) valueCount))
-    | hb_filter (glyphset, hb_first)
-    | hb_map_retains_sorting ([&] (const hb_pair_t<hb_codepoint_t, unsigned>& _)
-			      {
-				return hb_pair (glyph_map[_.first],
-						values_array.sub_array (_.second * sub_length,
-									sub_length));
-			      })
-    ;
-
-    bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
-    return_trace (ret);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  coverage.sanitize (c, this) &&
-		  valueFormat.sanitize_values (c, this, values, valueCount));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 2 */
-  Offset16To<Coverage>
-		coverage;		/* Offset to Coverage table--from
-					 * beginning of subtable */
-  ValueFormat	valueFormat;		/* Defines the types of data in the
-					 * ValueRecord */
-  HBUINT16	valueCount;		/* Number of ValueRecords */
-  ValueRecord	values;			/* Array of ValueRecords--positioning
-					 * values applied to glyphs */
-  public:
-  DEFINE_SIZE_ARRAY (8, values);
-};
-
-struct SinglePos
-{
-  template<typename Iterator,
-	   hb_requires (hb_is_iterator (Iterator))>
-  unsigned get_format (Iterator glyph_val_iter_pairs)
-  {
-    hb_array_t<const Value> first_val_iter = hb_second (*glyph_val_iter_pairs);
-
-    for (const auto iter : glyph_val_iter_pairs)
-      for (const auto _ : hb_zip (iter.second, first_val_iter))
-	if (_.first != _.second)
-	  return 2;
-
-    return 1;
-  }
-
-
-  template<typename Iterator,
-      typename SrcLookup,
-      hb_requires (hb_is_iterator (Iterator))>
-  void serialize (hb_serialize_context_t *c,
-		  const SrcLookup* src,
-		  Iterator glyph_val_iter_pairs,
-		  const hb_map_t *layout_variation_idx_map)
-  {
-    if (unlikely (!c->extend_min (u.format))) return;
-    unsigned format = 2;
-    ValueFormat new_format = src->get_value_format ();
-
-    if (glyph_val_iter_pairs)
-    {
-      format = get_format (glyph_val_iter_pairs);
-      new_format = src->get_value_format ().get_effective_format (+ glyph_val_iter_pairs | hb_map (hb_second));
-    }
-
-    u.format = format;
-    switch (u.format) {
-    case 1: u.format1.serialize (c,
-                                 src,
-                                 glyph_val_iter_pairs,
-                                 new_format,
-                                 layout_variation_idx_map);
-      return;
-    case 2: u.format2.serialize (c,
-                                 src,
-                                 glyph_val_iter_pairs,
-                                 new_format,
-                                 layout_variation_idx_map);
-      return;
-    default:return;
-    }
-  }
-
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
-  {
-    TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
-    switch (u.format) {
-    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
-    case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
-    default:return_trace (c->default_return_value ());
-    }
-  }
-
-  protected:
-  union {
-  HBUINT16		format;		/* Format identifier */
-  SinglePosFormat1	format1;
-  SinglePosFormat2	format2;
-  } u;
-};
-
-template<typename Iterator, typename SrcLookup>
-static void
-SinglePos_serialize (hb_serialize_context_t *c,
-		     const SrcLookup *src,
-		     Iterator it,
-		     const hb_map_t *layout_variation_idx_map)
-{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_map); }
-
-
-struct PairValueRecord
-{
-  friend struct PairSet;
-
-  int cmp (hb_codepoint_t k) const
-  { return secondGlyph.cmp (k); }
-
-  struct context_t
-  {
-    const void 		*base;
-    const ValueFormat	*valueFormats;
-    const ValueFormat	*newFormats;
-    unsigned		len1; /* valueFormats[0].get_len() */
-    const hb_map_t 	*glyph_map;
-    const hb_map_t      *layout_variation_idx_map;
-  };
-
-  bool subset (hb_subset_context_t *c,
-               context_t *closure) const
-  {
-    TRACE_SERIALIZE (this);
-    auto *s = c->serializer;
-    auto *out = s->start_embed (*this);
-    if (unlikely (!s->extend_min (out))) return_trace (false);
-
-    out->secondGlyph = (*closure->glyph_map)[secondGlyph];
-
-    closure->valueFormats[0].copy_values (s,
-                                          closure->newFormats[0],
-                                          closure->base, &values[0],
-                                          closure->layout_variation_idx_map);
-    closure->valueFormats[1].copy_values (s,
-                                          closure->newFormats[1],
-                                          closure->base,
-                                          &values[closure->len1],
-                                          closure->layout_variation_idx_map);
-
-    return_trace (true);
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-				  const ValueFormat *valueFormats,
-				  const void *base) const
-  {
-    unsigned record1_len = valueFormats[0].get_len ();
-    unsigned record2_len = valueFormats[1].get_len ();
-    const hb_array_t<const Value> values_array = values.as_array (record1_len + record2_len);
-
-    if (valueFormats[0].has_device ())
-      valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
-
-    if (valueFormats[1].has_device ())
-      valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
-  }
-
-  bool intersects (const hb_set_t& glyphset) const
-  {
-    return glyphset.has(secondGlyph);
-  }
-
-  const Value* get_values_1 () const
-  {
-    return &values[0];
-  }
-
-  const Value* get_values_2 (ValueFormat format1) const
-  {
-    return &values[format1.get_len ()];
-  }
-
-  protected:
-  HBGlyphID16	secondGlyph;		/* GlyphID of second glyph in the
-					 * pair--first glyph is listed in the
-					 * Coverage table */
-  ValueRecord	values;			/* Positioning data for the first glyph
-					 * followed by for second glyph */
-  public:
-  DEFINE_SIZE_ARRAY (2, values);
-};
-
-struct PairSet
-{
-  friend struct PairPosFormat1;
-
-  bool intersects (const hb_set_t *glyphs,
-		   const ValueFormat *valueFormats) const
-  {
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    unsigned int count = len;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (glyphs->has (record->secondGlyph))
-	return true;
-      record = &StructAtOffset<const PairValueRecord> (record, record_size);
-    }
-    return false;
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c,
-		       const ValueFormat *valueFormats) const
-  {
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    c->input->add_array (&record->secondGlyph, len, record_size);
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-				  const ValueFormat *valueFormats) const
-  {
-    unsigned len1 = valueFormats[0].get_len ();
-    unsigned len2 = valueFormats[1].get_len ();
-    unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    unsigned count = len;
-    for (unsigned i = 0; i < count; i++)
-    {
-      if (c->glyph_set->has (record->secondGlyph))
-      { record->collect_variation_indices (c, valueFormats, this); }
-
-      record = &StructAtOffset<const PairValueRecord> (record, record_size);
-    }
-  }
-
-  bool apply (hb_ot_apply_context_t *c,
-	      const ValueFormat *valueFormats,
-	      unsigned int pos) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
-						&firstPairValueRecord,
-						len,
-						record_size);
-    if (record)
-    {
-      bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
-      bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
-      if (applied_first || applied_second)
-	buffer->unsafe_to_break (buffer->idx, pos + 1);
-      if (len2)
-	pos++;
-      buffer->idx = pos;
-      return_trace (true);
-    }
-    buffer->unsafe_to_concat (buffer->idx, pos + 1);
-    return_trace (false);
-  }
-
-  bool subset (hb_subset_context_t *c,
-	       const ValueFormat valueFormats[2],
-               const ValueFormat newFormats[2]) const
-  {
-    TRACE_SUBSET (this);
-    auto snap = c->serializer->snapshot ();
-
-    auto *out = c->serializer->start_embed (*this);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-    out->len = 0;
-
-    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 ();
-    unsigned len2 = valueFormats[1].get_len ();
-    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
-
-    PairValueRecord::context_t context =
-    {
-      this,
-      valueFormats,
-      newFormats,
-      len1,
-      &glyph_map,
-      c->plan->layout_variation_idx_map
-    };
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    unsigned count = len, num = 0;
-    for (unsigned i = 0; i < count; i++)
-    {
-      if (glyphset.has (record->secondGlyph)
-	 && record->subset (c, &context)) num++;
-      record = &StructAtOffset<const PairValueRecord> (record, record_size);
-    }
-
-    out->len = num;
-    if (!num) c->serializer->revert (snap);
-    return_trace (num);
-  }
-
-  struct sanitize_closure_t
-  {
-    const ValueFormat *valueFormats;
-    unsigned int len1; /* valueFormats[0].get_len() */
-    unsigned int stride; /* 1 + len1 + len2 */
-  };
-
-  bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
-  {
-    TRACE_SANITIZE (this);
-    if (!(c->check_struct (this)
-       && c->check_range (&firstPairValueRecord,
-			  len,
-			  HBUINT16::static_size,
-			  closure->stride))) return_trace (false);
-
-    unsigned int count = len;
-    const PairValueRecord *record = &firstPairValueRecord;
-    return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
-		  closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
-  }
-
-  protected:
-  HBUINT16		len;	/* Number of PairValueRecords */
-  PairValueRecord	firstPairValueRecord;
-				/* Array of PairValueRecords--ordered
-				 * by GlyphID of the second glyph */
-  public:
-  DEFINE_SIZE_MIN (2);
-};
-
-struct PairPosFormat1
-{
-  bool intersects (const hb_set_t *glyphs) const
-  {
-    return
-    + hb_zip (this+coverage, pairSet)
-    | hb_filter (*glyphs, hb_first)
-    | hb_map (hb_second)
-    | hb_map ([glyphs, this] (const Offset16To<PairSet> &_)
-	      { return (this+_).intersects (glyphs, valueFormat); })
-    | hb_any
-    ;
-  }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return;
-
-    auto it =
-    + hb_zip (this+coverage, pairSet)
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    ;
-
-    if (!it) return;
-    + it
-    | hb_map (hb_add (this))
-    | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); })
-    ;
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  {
-    if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
-    unsigned int count = pairSet.len;
-    for (unsigned int i = 0; i < count; i++)
-      (this+pairSet[i]).collect_glyphs (c, valueFormat);
-  }
-
-  const Coverage &get_coverage () const { return this+coverage; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
-
-    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
-    unsigned unsafe_to;
-    if (!skippy_iter.next (&unsafe_to))
-    {
-      buffer->unsafe_to_concat (buffer->idx, unsafe_to);
-      return_trace (false);
-    }
-
-    return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-
-    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);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-    out->format = format;
-    out->valueFormat[0] = valueFormat[0];
-    out->valueFormat[1] = valueFormat[1];
-    if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
-    {
-      hb_pair_t<unsigned, unsigned> newFormats = compute_effective_value_formats (glyphset);
-      out->valueFormat[0] = newFormats.first;
-      out->valueFormat[1] = newFormats.second;
-    }
-
-    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
-
-    + hb_zip (this+coverage, pairSet)
-    | hb_filter (glyphset, hb_first)
-    | hb_filter ([this, c, out] (const Offset16To<PairSet>& _)
-		 {
-                   auto snap = c->serializer->snapshot ();
-		   auto *o = out->pairSet.serialize_append (c->serializer);
-		   if (unlikely (!o)) return false;
-		   bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat);
-		   if (!ret)
-		   {
-		     out->pairSet.pop ();
-		     c->serializer->revert (snap);
-		   }
-		   return ret;
-		 },
-		 hb_second)
-    | hb_map (hb_first)
-    | hb_map (glyph_map)
-    | hb_sink (new_coverage)
-    ;
-
-    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
-
-    return_trace (bool (new_coverage));
-  }
-
-
-  hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset) const
-  {
-    unsigned len1 = valueFormat[0].get_len ();
-    unsigned len2 = valueFormat[1].get_len ();
-    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
-
-    unsigned format1 = 0;
-    unsigned format2 = 0;
-    for (const Offset16To<PairSet>& _ :
-             + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second))
-    {
-      const PairSet& set = (this + _);
-      const PairValueRecord *record = &set.firstPairValueRecord;
-
-      for (unsigned i = 0; i < set.len; i++)
-      {
-        if (record->intersects (glyphset))
-        {
-          format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ());
-          format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]));
-        }
-        record = &StructAtOffset<const PairValueRecord> (record, record_size);
-      }
-    }
-
-    return hb_pair (format1, format2);
-  }
-
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-
-    if (!c->check_struct (this)) return_trace (false);
-
-    unsigned int len1 = valueFormat[0].get_len ();
-    unsigned int len2 = valueFormat[1].get_len ();
-    PairSet::sanitize_closure_t closure =
-    {
-      valueFormat,
-      len1,
-      1 + len1 + len2
-    };
-
-    return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 1 */
-  Offset16To<Coverage>
-		coverage;		/* Offset to Coverage table--from
-					 * beginning of subtable */
-  ValueFormat	valueFormat[2];		/* [0] Defines the types of data in
-					 * ValueRecord1--for the first glyph
-					 * in the pair--may be zero (0) */
-					/* [1] Defines the types of data in
-					 * ValueRecord2--for the second glyph
-					 * in the pair--may be zero (0) */
-  Array16OfOffset16To<PairSet>
-		pairSet;		/* Array of PairSet tables
-					 * ordered by Coverage Index */
-  public:
-  DEFINE_SIZE_ARRAY (10, pairSet);
-};
-
-struct PairPosFormat2
-{
-  bool intersects (const hb_set_t *glyphs) const
-  {
-    return (this+coverage).intersects (glyphs) &&
-	   (this+classDef2).intersects (glyphs);
-  }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    if (!intersects (c->glyph_set)) return;
-    if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return;
-
-    hb_set_t klass1_glyphs, klass2_glyphs;
-    if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return;
-    if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return;
-
-    hb_set_t class1_set, class2_set;
-    for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage))
-    {
-      if (!klass1_glyphs.has (cp)) class1_set.add (0);
-      else
-      {
-        unsigned klass1 = (this+classDef1).get (cp);
-        class1_set.add (klass1);
-      }
-    }
-
-    class2_set.add (0);
-    for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs))
-    {
-      unsigned klass2 = (this+classDef2).get (cp);
-      class2_set.add (klass2);
-    }
-
-    if (class1_set.is_empty ()
-        || class2_set.is_empty ()
-        || (class2_set.get_population() == 1 && class2_set.has(0)))
-      return;
-
-    unsigned len1 = valueFormat1.get_len ();
-    unsigned len2 = valueFormat2.get_len ();
-    const hb_array_t<const Value> values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2));
-    for (const unsigned class1_idx : class1_set.iter ())
-    {
-      for (const unsigned class2_idx : class2_set.iter ())
-      {
-	unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
-	if (valueFormat1.has_device ())
-	  valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1));
-
-	if (valueFormat2.has_device ())
-	  valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2));
-      }
-    }
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  {
-    if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
-    if (unlikely (!(this+classDef2).collect_coverage (c->input))) return;
-  }
-
-  const Coverage &get_coverage () const { return this+coverage; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
-
-    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
-    unsigned unsafe_to;
-    if (!skippy_iter.next (&unsafe_to))
-    {
-      buffer->unsafe_to_concat (buffer->idx, unsafe_to);
-      return_trace (false);
-    }
-
-    unsigned int len1 = valueFormat1.get_len ();
-    unsigned int len2 = valueFormat2.get_len ();
-    unsigned int record_len = len1 + len2;
-
-    unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
-    unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
-    if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
-    {
-      buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
-      return_trace (false);
-    }
-
-    const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
-
-    bool applied_first = false, applied_second = false;
-
-
-    /* Isolate simple kerning and apply it half to each side.
-     * Results in better cursor positinoing / underline drawing.
-     *
-     * Disabled, because causes issues... :-(
-     * https://github.com/harfbuzz/harfbuzz/issues/3408
-     * https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978
-     */
-#ifndef HB_SPLIT_KERN
-    if (0)
-#endif
-    {
-      if (!len2)
-      {
-	const hb_direction_t dir = buffer->props.direction;
-	const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir);
-	const bool backward = HB_DIRECTION_IS_BACKWARD (dir);
-	unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance;
-	if (backward)
-	  mask |= mask >> 2; /* Add eg. xPlacement in RTL. */
-	/* Add Devices. */
-	mask |= mask << 4;
-
-	if (valueFormat1 & ~mask)
-	  goto bail;
-
-	/* Is simple kern. Apply value on an empty position slot,
-	 * then split it between sides. */
-
-	hb_glyph_position_t pos{};
-	if (valueFormat1.apply_value (c, this, v, pos))
-	{
-	  hb_position_t *src  = &pos.x_advance;
-	  hb_position_t *dst1 = &buffer->cur_pos().x_advance;
-	  hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance;
-	  unsigned i = horizontal ? 0 : 1;
-
-	  hb_position_t kern  = src[i];
-	  hb_position_t kern1 = kern >> 1;
-	  hb_position_t kern2 = kern - kern1;
-
-	  if (!backward)
-	  {
-	    dst1[i] += kern1;
-	    dst2[i] += kern2;
-	    dst2[i + 2] += kern2;
-	  }
-	  else
-	  {
-	    dst1[i] += kern1;
-	    dst1[i + 2] += src[i + 2] - kern2;
-	    dst2[i] += kern2;
-	  }
-
-	  applied_first = applied_second = kern != 0;
-	  goto success;
-	}
-	goto boring;
-      }
-    }
-    bail:
-
-
-    applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos());
-    applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
-
-    success:
-    if (applied_first || applied_second)
-      buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
-    else
-    boring:
-      buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
-
-
-    buffer->idx = skippy_iter.idx;
-    if (len2)
-      buffer->idx++;
-
-    return_trace (true);
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    auto *out = c->serializer->start_embed (*this);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-    out->format = format;
-
-    hb_map_t klass1_map;
-    out->classDef1.serialize_subset (c, classDef1, this, &klass1_map, true, true, &(this + coverage));
-    out->class1Count = klass1_map.get_population ();
-
-    hb_map_t klass2_map;
-    out->classDef2.serialize_subset (c, classDef2, this, &klass2_map, true, false);
-    out->class2Count = klass2_map.get_population ();
-
-    unsigned len1 = valueFormat1.get_len ();
-    unsigned len2 = valueFormat2.get_len ();
-
-    hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat1, valueFormat2);
-    if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
-      newFormats = compute_effective_value_formats (klass1_map, klass2_map);
-
-    out->valueFormat1 = newFormats.first;
-    out->valueFormat2 = newFormats.second;
-
-    for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
-    {
-      for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
-      {
-        unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
-        valueFormat1.copy_values (c->serializer, newFormats.first, this, &values[idx], c->plan->layout_variation_idx_map);
-        valueFormat2.copy_values (c->serializer, newFormats.second, this, &values[idx + len1], c->plan->layout_variation_idx_map);
-      }
-    }
-
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
-
-    auto it =
-    + hb_iter (this+coverage)
-    | hb_filter (glyphset)
-    | hb_map_retains_sorting (glyph_map)
-    ;
-
-    out->coverage.serialize_serialize (c->serializer, it);
-    return_trace (out->class1Count && out->class2Count && bool (it));
-  }
-
-
-  hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_map_t& klass1_map,
-                                                                 const hb_map_t& klass2_map) const
-  {
-    unsigned len1 = valueFormat1.get_len ();
-    unsigned len2 = valueFormat2.get_len ();
-
-    unsigned format1 = 0;
-    unsigned format2 = 0;
-
-    for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
-    {
-      for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
-      {
-        unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
-        format1 = format1 | valueFormat1.get_effective_format (&values[idx]);
-        format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]);
-      }
-    }
-
-    return hb_pair (format1, format2);
-  }
-
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (!(c->check_struct (this)
-       && coverage.sanitize (c, this)
-       && classDef1.sanitize (c, this)
-       && classDef2.sanitize (c, this))) return_trace (false);
-
-    unsigned int len1 = valueFormat1.get_len ();
-    unsigned int len2 = valueFormat2.get_len ();
-    unsigned int stride = len1 + len2;
-    unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
-    unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
-    return_trace (c->check_range ((const void *) values,
-				  count,
-				  record_size) &&
-		  valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
-		  valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 2 */
-  Offset16To<Coverage>
-		coverage;		/* Offset to Coverage table--from
-					 * beginning of subtable */
-  ValueFormat	valueFormat1;		/* ValueRecord definition--for the
-					 * first glyph of the pair--may be zero
-					 * (0) */
-  ValueFormat	valueFormat2;		/* ValueRecord definition--for the
-					 * second glyph of the pair--may be
-					 * zero (0) */
-  Offset16To<ClassDef>
-		classDef1;		/* Offset to ClassDef table--from
-					 * beginning of PairPos subtable--for
-					 * the first glyph of the pair */
-  Offset16To<ClassDef>
-		classDef2;		/* Offset to ClassDef table--from
-					 * beginning of PairPos subtable--for
-					 * the second glyph of the pair */
-  HBUINT16	class1Count;		/* Number of classes in ClassDef1
-					 * table--includes Class0 */
-  HBUINT16	class2Count;		/* Number of classes in ClassDef2
-					 * table--includes Class0 */
-  ValueRecord	values;			/* Matrix of value pairs:
-					 * class1-major, class2-minor,
-					 * Each entry has value1 and value2 */
-  public:
-  DEFINE_SIZE_ARRAY (16, values);
-};
-
-struct PairPos
-{
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
-  {
-    TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
-    switch (u.format) {
-    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
-    case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
-    default:return_trace (c->default_return_value ());
-    }
-  }
-
-  protected:
-  union {
-  HBUINT16		format;		/* Format identifier */
-  PairPosFormat1	format1;
-  PairPosFormat2	format2;
-  } u;
-};
-
-
-struct EntryExitRecord
-{
-  friend struct CursivePosFormat1;
-
-  bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-				  const void *src_base) const
-  {
-    (src_base+entryAnchor).collect_variation_indices (c);
-    (src_base+exitAnchor).collect_variation_indices (c);
-  }
-
-  EntryExitRecord* subset (hb_subset_context_t *c,
-                           const void *src_base) const
-  {
-    TRACE_SERIALIZE (this);
-    auto *out = c->serializer->embed (this);
-    if (unlikely (!out)) return_trace (nullptr);
-
-    out->entryAnchor.serialize_subset (c, entryAnchor, src_base);
-    out->exitAnchor.serialize_subset (c, exitAnchor, src_base);
-    return_trace (out);
-  }
-
-  protected:
-  Offset16To<Anchor>
-		entryAnchor;		/* Offset to EntryAnchor table--from
-					 * beginning of CursivePos
-					 * subtable--may be NULL */
-  Offset16To<Anchor>
-		exitAnchor;		/* Offset to ExitAnchor table--from
-					 * beginning of CursivePos
-					 * subtable--may be NULL */
-  public:
-  DEFINE_SIZE_STATIC (4);
-};
-
-static void
-reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent);
-
-struct CursivePosFormat1
-{
-  bool intersects (const hb_set_t *glyphs) const
-  { return (this+coverage).intersects (glyphs); }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    + hb_zip (this+coverage, entryExitRecord)
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); })
-    ;
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
-
-  const Coverage &get_coverage () const { return this+coverage; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-
-    const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
-    if (!this_record.entryAnchor) return_trace (false);
-
-    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
-    unsigned unsafe_from;
-    if (!skippy_iter.prev (&unsafe_from))
-    {
-      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
-    if (!prev_record.exitAnchor)
-    {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    unsigned int i = skippy_iter.idx;
-    unsigned int j = buffer->idx;
-
-    buffer->unsafe_to_break (i, j);
-    float entry_x, entry_y, exit_x, exit_y;
-    (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
-    (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
-
-    hb_glyph_position_t *pos = buffer->pos;
-
-    hb_position_t d;
-    /* Main-direction adjustment */
-    switch (c->direction) {
-      case HB_DIRECTION_LTR:
-	pos[i].x_advance  = roundf (exit_x) + pos[i].x_offset;
-
-	d = roundf (entry_x) + pos[j].x_offset;
-	pos[j].x_advance -= d;
-	pos[j].x_offset  -= d;
-	break;
-      case HB_DIRECTION_RTL:
-	d = roundf (exit_x) + pos[i].x_offset;
-	pos[i].x_advance -= d;
-	pos[i].x_offset  -= d;
-
-	pos[j].x_advance  = roundf (entry_x) + pos[j].x_offset;
-	break;
-      case HB_DIRECTION_TTB:
-	pos[i].y_advance  = roundf (exit_y) + pos[i].y_offset;
-
-	d = roundf (entry_y) + pos[j].y_offset;
-	pos[j].y_advance -= d;
-	pos[j].y_offset  -= d;
-	break;
-      case HB_DIRECTION_BTT:
-	d = roundf (exit_y) + pos[i].y_offset;
-	pos[i].y_advance -= d;
-	pos[i].y_offset  -= d;
-
-	pos[j].y_advance  = roundf (entry_y);
-	break;
-      case HB_DIRECTION_INVALID:
-      default:
-	break;
-    }
-
-    /* Cross-direction adjustment */
-
-    /* We attach child to parent (think graph theory and rooted trees whereas
-     * the root stays on baseline and each node aligns itself against its
-     * parent.
-     *
-     * Optimize things for the case of RightToLeft, as that's most common in
-     * Arabic. */
-    unsigned int child  = i;
-    unsigned int parent = j;
-    hb_position_t x_offset = entry_x - exit_x;
-    hb_position_t y_offset = entry_y - exit_y;
-    if  (!(c->lookup_props & LookupFlag::RightToLeft))
-    {
-      unsigned int k = child;
-      child = parent;
-      parent = k;
-      x_offset = -x_offset;
-      y_offset = -y_offset;
-    }
-
-    /* If child was already connected to someone else, walk through its old
-     * chain and reverse the link direction, such that the whole tree of its
-     * previous connection now attaches to new parent.  Watch out for case
-     * where new parent is on the path from old chain...
-     */
-    reverse_cursive_minor_offset (pos, child, c->direction, parent);
-
-    pos[child].attach_type() = ATTACH_TYPE_CURSIVE;
-    pos[child].attach_chain() = (int) parent - (int) child;
-    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
-    if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
-      pos[child].y_offset = y_offset;
-    else
-      pos[child].x_offset = x_offset;
-
-    /* If parent was attached to child, separate them.
-     * https://github.com/harfbuzz/harfbuzz/issues/2469
-     */
-    if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain()))
-      pos[parent].attach_chain() = 0;
-
-    buffer->idx++;
-    return_trace (true);
-  }
-
-  template <typename Iterator,
-	    hb_requires (hb_is_iterator (Iterator))>
-  void serialize (hb_subset_context_t *c,
-		  Iterator it,
-		  const void *src_base)
-  {
-    if (unlikely (!c->serializer->extend_min ((*this)))) return;
-    this->format = 1;
-    this->entryExitRecord.len = it.len ();
-
-    for (const EntryExitRecord& entry_record : + it
-					       | hb_map (hb_second))
-      entry_record.subset (c, src_base);
-
-    auto glyphs =
-    + it
-    | hb_map_retains_sorting (hb_first)
-    ;
-
-    coverage.serialize_serialize (c->serializer, glyphs);
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    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);
-    if (unlikely (!out)) return_trace (false);
-
-    auto it =
-    + hb_zip (this+coverage, entryExitRecord)
-    | hb_filter (glyphset, hb_first)
-    | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&>
-			      { return hb_pair (glyph_map[p.first], p.second);})
-    ;
-
-    bool ret = bool (it);
-    out->serialize (c, it, this);
-    return_trace (ret);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 1 */
-  Offset16To<Coverage>
-		coverage;		/* Offset to Coverage table--from
-					 * beginning of subtable */
-  Array16Of<EntryExitRecord>
-		entryExitRecord;	/* Array of EntryExit records--in
-					 * Coverage Index order */
-  public:
-  DEFINE_SIZE_ARRAY (6, entryExitRecord);
-};
-
-struct CursivePos
-{
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
-  {
-    TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
-    switch (u.format) {
-    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
-    default:return_trace (c->default_return_value ());
-    }
-  }
-
-  protected:
-  union {
-  HBUINT16		format;		/* Format identifier */
-  CursivePosFormat1	format1;
-  } u;
-};
-
-
-typedef AnchorMatrix BaseArray;		/* base-major--
-					 * in order of BaseCoverage Index--,
-					 * mark-minor--
-					 * ordered by class--zero-based. */
-
-static void Markclass_closure_and_remap_indexes (const Coverage  &mark_coverage,
-						 const MarkArray &mark_array,
-						 const hb_set_t  &glyphset,
-						 hb_map_t*        klass_mapping /* INOUT */)
-{
-  hb_set_t orig_classes;
-
-  + hb_zip (mark_coverage, mark_array)
-  | hb_filter (glyphset, hb_first)
-  | hb_map (hb_second)
-  | hb_map (&MarkRecord::get_class)
-  | hb_sink (orig_classes)
-  ;
-
-  unsigned idx = 0;
-  for (auto klass : orig_classes.iter ())
-  {
-    if (klass_mapping->has (klass)) continue;
-    klass_mapping->set (klass, idx);
-    idx++;
-  }
-}
-
-struct MarkBasePosFormat1
-{
-  bool intersects (const hb_set_t *glyphs) const
-  {
-    return (this+markCoverage).intersects (glyphs) &&
-	   (this+baseCoverage).intersects (glyphs);
-  }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    + hb_zip (this+markCoverage, this+markArray)
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); })
-    ;
-
-    hb_map_t klass_mapping;
-    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping);
-
-    unsigned basecount = (this+baseArray).rows;
-    auto base_iter =
-    + hb_zip (this+baseCoverage, hb_range (basecount))
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    ;
-
-    hb_sorted_vector_t<unsigned> base_indexes;
-    for (const unsigned row : base_iter)
-    {
-      + hb_range ((unsigned) classCount)
-      | hb_filter (klass_mapping)
-      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
-      | hb_sink (base_indexes)
-      ;
-    }
-    (this+baseArray).collect_variation_indices (c, base_indexes.iter ());
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  {
-    if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return;
-    if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return;
-  }
-
-  const Coverage &get_coverage () const { return this+markCoverage; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (mark_index == NOT_COVERED)) return_trace (false);
-
-    /* Now we search backwards for a non-mark glyph */
-    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
-    skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
-    do {
-      unsigned unsafe_from;
-      if (!skippy_iter.prev (&unsafe_from))
-      {
-	buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
-	return_trace (false);
-      }
-
-      /* We only want to attach to the first of a MultipleSubst sequence.
-       * https://github.com/harfbuzz/harfbuzz/issues/740
-       * Reject others...
-       * ...but stop if we find a mark in the MultipleSubst sequence:
-       * https://github.com/harfbuzz/harfbuzz/issues/1020 */
-      if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
-	  0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
-	  (skippy_iter.idx == 0 ||
-	   _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
-	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
-	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
-	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
-	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
-	   ))
-	break;
-      skippy_iter.reject ();
-    } while (true);
-
-    /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
-    //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
-
-    unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
-    if (base_index == NOT_COVERED)
-    {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    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);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-    out->format = format;
-
-    hb_map_t klass_mapping;
-    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
-
-    if (!klass_mapping.get_population ()) return_trace (false);
-    out->classCount = klass_mapping.get_population ();
-
-    auto mark_iter =
-    + hb_zip (this+markCoverage, this+markArray)
-    | hb_filter (glyphset, hb_first)
-    ;
-
-    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
-    + mark_iter
-    | hb_map (hb_first)
-    | hb_map (glyph_map)
-    | hb_sink (new_coverage)
-    ;
-
-    if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
-      return_trace (false);
-
-    out->markArray.serialize_subset (c, markArray, this,
-                                     (this+markCoverage).iter (),
-                                     &klass_mapping);
-
-    unsigned basecount = (this+baseArray).rows;
-    auto base_iter =
-    + hb_zip (this+baseCoverage, hb_range (basecount))
-    | hb_filter (glyphset, hb_first)
-    ;
-
-    new_coverage.reset ();
-    + base_iter
-    | hb_map (hb_first)
-    | hb_map (glyph_map)
-    | hb_sink (new_coverage)
-    ;
-
-    if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
-      return_trace (false);
-
-    hb_sorted_vector_t<unsigned> base_indexes;
-    for (const unsigned row : + base_iter
-			      | hb_map (hb_second))
-    {
-      + hb_range ((unsigned) classCount)
-      | hb_filter (klass_mapping)
-      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
-      | hb_sink (base_indexes)
-      ;
-    }
-
-    out->baseArray.serialize_subset (c, baseArray, this,
-                                     base_iter.len (),
-                                     base_indexes.iter ());
-
-    return_trace (true);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  markCoverage.sanitize (c, this) &&
-		  baseCoverage.sanitize (c, this) &&
-		  markArray.sanitize (c, this) &&
-		  baseArray.sanitize (c, this, (unsigned int) classCount));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 1 */
-  Offset16To<Coverage>
-		markCoverage;		/* Offset to MarkCoverage table--from
-					 * beginning of MarkBasePos subtable */
-  Offset16To<Coverage>
-		baseCoverage;		/* Offset to BaseCoverage table--from
-					 * beginning of MarkBasePos subtable */
-  HBUINT16	classCount;		/* Number of classes defined for marks */
-  Offset16To<MarkArray>
-		markArray;		/* Offset to MarkArray table--from
-					 * beginning of MarkBasePos subtable */
-  Offset16To<BaseArray>
-		baseArray;		/* Offset to BaseArray table--from
-					 * beginning of MarkBasePos subtable */
-  public:
-  DEFINE_SIZE_STATIC (12);
-};
-
-struct MarkBasePos
-{
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
-  {
-    TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
-    switch (u.format) {
-    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
-    default:return_trace (c->default_return_value ());
-    }
-  }
-
-  protected:
-  union {
-  HBUINT16		format;		/* Format identifier */
-  MarkBasePosFormat1	format1;
-  } u;
-};
-
-
-typedef AnchorMatrix LigatureAttach;	/* component-major--
-					 * in order of writing direction--,
-					 * mark-minor--
-					 * ordered by class--zero-based. */
-
-/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
-struct LigatureArray : List16OfOffset16To<LigatureAttach>
-{
-  template <typename Iterator,
-	    hb_requires (hb_is_iterator (Iterator))>
-  bool subset (hb_subset_context_t *c,
-               Iterator		    coverage,
-	       unsigned		    class_count,
-	       const hb_map_t	   *klass_mapping) const
-  {
-    TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-
-    auto *out = c->serializer->start_embed (this);
-    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
-
-    for (const auto _ : + hb_zip (coverage, *this)
-		  | hb_filter (glyphset, hb_first))
-    {
-      auto *matrix = out->serialize_append (c->serializer);
-      if (unlikely (!matrix)) return_trace (false);
-
-      const LigatureAttach& src = (this + _.second);
-      auto indexes =
-          + hb_range (src.rows * class_count)
-          | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
-          ;
-      matrix->serialize_subset (c,
-				_.second,
-				this,
-                                src.rows,
-                                indexes);
-    }
-    return_trace (this->len);
-  }
-};
-
-struct MarkLigPosFormat1
-{
-  bool intersects (const hb_set_t *glyphs) const
-  {
-    return (this+markCoverage).intersects (glyphs) &&
-	   (this+ligatureCoverage).intersects (glyphs);
-  }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    + hb_zip (this+markCoverage, this+markArray)
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); })
-    ;
-
-    hb_map_t klass_mapping;
-    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping);
-
-    unsigned ligcount = (this+ligatureArray).len;
-    auto lig_iter =
-    + hb_zip (this+ligatureCoverage, hb_range (ligcount))
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    ;
-
-    const LigatureArray& lig_array = this+ligatureArray;
-    for (const unsigned i : lig_iter)
-    {
-      hb_sorted_vector_t<unsigned> lig_indexes;
-      unsigned row_count = lig_array[i].rows;
-      for (unsigned row : + hb_range (row_count))
-      {
-	+ hb_range ((unsigned) classCount)
-	| hb_filter (klass_mapping)
-	| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
-	| hb_sink (lig_indexes)
-	;
-      }
-
-      lig_array[i].collect_variation_indices (c, lig_indexes.iter ());
-    }
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  {
-    if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return;
-    if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return;
-  }
-
-  const Coverage &get_coverage () const { return this+markCoverage; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (mark_index == NOT_COVERED)) return_trace (false);
-
-    /* Now we search backwards for a non-mark glyph */
-    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
-    skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
-    unsigned unsafe_from;
-    if (!skippy_iter.prev (&unsafe_from))
-    {
-      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
-    //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
-
-    unsigned int j = skippy_iter.idx;
-    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[j].codepoint);
-    if (lig_index == NOT_COVERED)
-    {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    const LigatureArray& lig_array = this+ligatureArray;
-    const LigatureAttach& lig_attach = lig_array[lig_index];
-
-    /* Find component to attach to */
-    unsigned int comp_count = lig_attach.rows;
-    if (unlikely (!comp_count))
-    {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    /* We must now check whether the ligature ID of the current mark glyph
-     * is identical to the ligature ID of the found ligature.  If yes, we
-     * can directly use the component index.  If not, we attach the mark
-     * glyph to the last component of the ligature. */
-    unsigned int comp_index;
-    unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]);
-    unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
-    unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
-    if (lig_id && lig_id == mark_id && mark_comp > 0)
-      comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1;
-    else
-      comp_index = comp_count - 1;
-
-    return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    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);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-    out->format = format;
-
-    hb_map_t klass_mapping;
-    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
-
-    if (!klass_mapping.get_population ()) return_trace (false);
-    out->classCount = klass_mapping.get_population ();
-
-    auto mark_iter =
-    + hb_zip (this+markCoverage, this+markArray)
-    | hb_filter (glyphset, hb_first)
-    ;
-
-    auto new_mark_coverage =
-    + mark_iter
-    | hb_map_retains_sorting (hb_first)
-    | hb_map_retains_sorting (glyph_map)
-    ;
-
-    if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage))
-      return_trace (false);
-
-    out->markArray.serialize_subset (c, markArray, this,
-                                     (this+markCoverage).iter (),
-                                     &klass_mapping);
-
-    auto new_ligature_coverage =
-    + hb_iter (this + ligatureCoverage)
-    | hb_filter (glyphset)
-    | hb_map_retains_sorting (glyph_map)
-    ;
-
-    if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage))
-      return_trace (false);
-
-    out->ligatureArray.serialize_subset (c, ligatureArray, this,
-                                         hb_iter (this+ligatureCoverage), classCount, &klass_mapping);
-
-    return_trace (true);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  markCoverage.sanitize (c, this) &&
-		  ligatureCoverage.sanitize (c, this) &&
-		  markArray.sanitize (c, this) &&
-		  ligatureArray.sanitize (c, this, (unsigned int) classCount));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 1 */
-  Offset16To<Coverage>
-		markCoverage;		/* Offset to Mark Coverage table--from
-					 * beginning of MarkLigPos subtable */
-  Offset16To<Coverage>
-		ligatureCoverage;	/* Offset to Ligature Coverage
-					 * table--from beginning of MarkLigPos
-					 * subtable */
-  HBUINT16	classCount;		/* Number of defined mark classes */
-  Offset16To<MarkArray>
-		markArray;		/* Offset to MarkArray table--from
-					 * beginning of MarkLigPos subtable */
-  Offset16To<LigatureArray>
-		ligatureArray;		/* Offset to LigatureArray table--from
-					 * beginning of MarkLigPos subtable */
-  public:
-  DEFINE_SIZE_STATIC (12);
-};
-
-
-struct MarkLigPos
-{
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
-  {
-    TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
-    switch (u.format) {
-    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
-    default:return_trace (c->default_return_value ());
-    }
-  }
-
-  protected:
-  union {
-  HBUINT16		format;		/* Format identifier */
-  MarkLigPosFormat1	format1;
-  } u;
-};
-
-
-typedef AnchorMatrix Mark2Array;	/* mark2-major--
-					 * in order of Mark2Coverage Index--,
-					 * mark1-minor--
-					 * ordered by class--zero-based. */
-
-struct MarkMarkPosFormat1
-{
-  bool intersects (const hb_set_t *glyphs) const
-  {
-    return (this+mark1Coverage).intersects (glyphs) &&
-	   (this+mark2Coverage).intersects (glyphs);
-  }
-
-  void closure_lookups (hb_closure_lookups_context_t *c) const {}
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    + hb_zip (this+mark1Coverage, this+mark1Array)
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); })
-    ;
-
-    hb_map_t klass_mapping;
-    Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping);
-
-    unsigned mark2_count = (this+mark2Array).rows;
-    auto mark2_iter =
-    + hb_zip (this+mark2Coverage, hb_range (mark2_count))
-    | hb_filter (c->glyph_set, hb_first)
-    | hb_map (hb_second)
-    ;
-
-    hb_sorted_vector_t<unsigned> mark2_indexes;
-    for (const unsigned row : mark2_iter)
-    {
-      + hb_range ((unsigned) classCount)
-      | hb_filter (klass_mapping)
-      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
-      | hb_sink (mark2_indexes)
-      ;
-    }
-    (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ());
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  {
-    if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return;
-    if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return;
-  }
-
-  const Coverage &get_coverage () const { return this+mark1Coverage; }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int mark1_index = (this+mark1Coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (mark1_index == NOT_COVERED)) return_trace (false);
-
-    /* now we search backwards for a suitable mark glyph until a non-mark glyph */
-    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
-    skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
-    unsigned unsafe_from;
-    if (!skippy_iter.prev (&unsafe_from))
-    {
-      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]))
-    {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    unsigned int j = skippy_iter.idx;
-
-    unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur());
-    unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]);
-    unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur());
-    unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]);
-
-    if (likely (id1 == id2))
-    {
-      if (id1 == 0) /* Marks belonging to the same base. */
-	goto good;
-      else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
-	goto good;
-    }
-    else
-    {
-      /* If ligature ids don't match, it may be the case that one of the marks
-       * itself is a ligature.  In which case match. */
-      if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
-	goto good;
-    }
-
-    /* Didn't match. */
-    buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
-    return_trace (false);
-
-    good:
-    unsigned int mark2_index = (this+mark2Coverage).get_coverage  (buffer->info[j].codepoint);
-    if (mark2_index == NOT_COVERED)
-    {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
-      return_trace (false);
-    }
-
-    return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
-  }
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    TRACE_SUBSET (this);
-    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);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-    out->format = format;
-
-    hb_map_t klass_mapping;
-    Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping);
-
-    if (!klass_mapping.get_population ()) return_trace (false);
-    out->classCount = klass_mapping.get_population ();
-
-    auto mark1_iter =
-    + hb_zip (this+mark1Coverage, this+mark1Array)
-    | hb_filter (glyphset, hb_first)
-    ;
-
-    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
-    + mark1_iter
-    | hb_map (hb_first)
-    | hb_map (glyph_map)
-    | hb_sink (new_coverage)
-    ;
-
-    if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
-      return_trace (false);
-
-    out->mark1Array.serialize_subset (c, mark1Array, this,
-                                      (this+mark1Coverage).iter (),
-                                      &klass_mapping);
-
-    unsigned mark2count = (this+mark2Array).rows;
-    auto mark2_iter =
-    + hb_zip (this+mark2Coverage, hb_range (mark2count))
-    | hb_filter (glyphset, hb_first)
-    ;
-
-    new_coverage.reset ();
-    + mark2_iter
-    | hb_map (hb_first)
-    | hb_map (glyph_map)
-    | hb_sink (new_coverage)
-    ;
-
-    if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
-      return_trace (false);
-
-    hb_sorted_vector_t<unsigned> mark2_indexes;
-    for (const unsigned row : + mark2_iter
-			      | hb_map (hb_second))
-    {
-      + hb_range ((unsigned) classCount)
-      | hb_filter (klass_mapping)
-      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
-      | hb_sink (mark2_indexes)
-      ;
-    }
-
-    out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ());
-
-    return_trace (true);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  mark1Coverage.sanitize (c, this) &&
-		  mark2Coverage.sanitize (c, this) &&
-		  mark1Array.sanitize (c, this) &&
-		  mark2Array.sanitize (c, this, (unsigned int) classCount));
-  }
-
-  protected:
-  HBUINT16	format;			/* Format identifier--format = 1 */
-  Offset16To<Coverage>
-		mark1Coverage;		/* Offset to Combining Mark1 Coverage
-					 * table--from beginning of MarkMarkPos
-					 * subtable */
-  Offset16To<Coverage>
-		mark2Coverage;		/* Offset to Combining Mark2 Coverage
-					 * table--from beginning of MarkMarkPos
-					 * subtable */
-  HBUINT16	classCount;		/* Number of defined mark classes */
-  Offset16To<MarkArray>
-		mark1Array;		/* Offset to Mark1Array table--from
-					 * beginning of MarkMarkPos subtable */
-  Offset16To<Mark2Array>
-		mark2Array;		/* Offset to Mark2Array table--from
-					 * beginning of MarkMarkPos subtable */
-  public:
-  DEFINE_SIZE_STATIC (12);
-};
-
-struct MarkMarkPos
-{
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
-  {
-    TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
-    switch (u.format) {
-    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
-    default:return_trace (c->default_return_value ());
-    }
-  }
-
-  protected:
-  union {
-  HBUINT16		format;		/* Format identifier */
-  MarkMarkPosFormat1	format1;
-  } u;
-};
-
-
-struct ContextPos : Context {};
-
-struct ChainContextPos : ChainContext {};
-
-struct ExtensionPos : Extension<ExtensionPos>
-{
-  typedef struct PosLookupSubTable SubTable;
-};
-
-
-
-/*
- * PosLookup
- */
-
-
-struct PosLookupSubTable
-{
-  friend struct Lookup;
-  friend struct PosLookup;
-
-  enum Type {
-    Single		= 1,
-    Pair		= 2,
-    Cursive		= 3,
-    MarkBase		= 4,
-    MarkLig		= 5,
-    MarkMark		= 6,
-    Context		= 7,
-    ChainContext	= 8,
-    Extension		= 9
-  };
-
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
-  {
-    TRACE_DISPATCH (this, lookup_type);
-    switch (lookup_type) {
-    case Single:		return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
-    case Pair:			return_trace (u.pair.dispatch (c, std::forward<Ts> (ds)...));
-    case Cursive:		return_trace (u.cursive.dispatch (c, std::forward<Ts> (ds)...));
-    case MarkBase:		return_trace (u.markBase.dispatch (c, std::forward<Ts> (ds)...));
-    case MarkLig:		return_trace (u.markLig.dispatch (c, std::forward<Ts> (ds)...));
-    case MarkMark:		return_trace (u.markMark.dispatch (c, std::forward<Ts> (ds)...));
-    case Context:		return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
-    case ChainContext:		return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
-    case Extension:		return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
-    default:			return_trace (c->default_return_value ());
-    }
-  }
-
-  bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
-  {
-    hb_intersects_context_t c (glyphs);
-    return dispatch (&c, lookup_type);
-  }
-
-  protected:
-  union {
-  SinglePos		single;
-  PairPos		pair;
-  CursivePos		cursive;
-  MarkBasePos		markBase;
-  MarkLigPos		markLig;
-  MarkMarkPos		markMark;
-  ContextPos		context;
-  ChainContextPos	chainContext;
-  ExtensionPos		extension;
-  } u;
-  public:
-  DEFINE_SIZE_MIN (0);
-};
-
-
-struct PosLookup : Lookup
-{
-  typedef struct PosLookupSubTable SubTable;
-
-  const SubTable& get_subtable (unsigned int i) const
-  { return Lookup::get_subtable<SubTable> (i); }
-
-  bool is_reverse () const
-  {
-    return false;
-  }
-
-  bool apply (hb_ot_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    return_trace (dispatch (c));
-  }
-
-  bool intersects (const hb_set_t *glyphs) const
-  {
-    hb_intersects_context_t c (glyphs);
-    return dispatch (&c);
-  }
-
-  hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
-  { return dispatch (c); }
-
-  hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
-  {
-    if (c->is_lookup_visited (this_index))
-      return hb_closure_lookups_context_t::default_return_value ();
-
-    c->set_lookup_visited (this_index);
-    if (!intersects (c->glyphs))
-    {
-      c->set_lookup_inactive (this_index);
-      return hb_closure_lookups_context_t::default_return_value ();
-    }
-    c->set_recurse_func (dispatch_closure_lookups_recurse_func);
-
-    hb_closure_lookups_context_t::return_t ret = dispatch (c);
-    return ret;
-  }
-
-  template <typename set_t>
-  void collect_coverage (set_t *glyphs) const
-  {
-    hb_collect_coverage_context_t<set_t> c (glyphs);
-    dispatch (&c);
-  }
-
-  static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
-
-  template <typename context_t>
-  static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
-
-  HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index);
-
-  template <typename context_t, typename ...Ts>
-  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
-  { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
-
-  bool subset (hb_subset_context_t *c) const
-  { return Lookup::subset<SubTable> (c); }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  { return Lookup::sanitize<SubTable> (c); }
-};
-
-/*
- * GPOS -- Glyph Positioning
- * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
- */
-
-struct GPOS : GSUBGPOS
-{
-  static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS;
-
-  const PosLookup& get_lookup (unsigned int i) const
-  { return static_cast<const PosLookup &> (GSUBGPOS::get_lookup (i)); }
-
-  static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
-  static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
-  static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
-
-  bool subset (hb_subset_context_t *c) const
-  {
-    hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features);
-    return GSUBGPOS::subset<PosLookup> (&l);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  { return GSUBGPOS::sanitize<PosLookup> (c); }
-
-  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
-				   hb_face_t *face) const;
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
-  {
-    for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++)
-    {
-      if (!c->gpos_lookups->has (i)) continue;
-      const PosLookup &l = get_lookup (i);
-      l.dispatch (c);
-    }
-  }
-
-  void closure_lookups (hb_face_t      *face,
-			const hb_set_t *glyphs,
-			hb_set_t       *lookup_indexes /* IN/OUT */) const
-  { GSUBGPOS::closure_lookups<PosLookup> (face, glyphs, lookup_indexes); }
-
-  typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
-};
-
-
-static void
-reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent)
-{
-  int chain = pos[i].attach_chain(), type = pos[i].attach_type();
-  if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
-    return;
-
-  pos[i].attach_chain() = 0;
-
-  unsigned int j = (int) i + chain;
-
-  /* Stop if we see new parent in the chain. */
-  if (j == new_parent)
-    return;
-
-  reverse_cursive_minor_offset (pos, j, direction, new_parent);
-
-  if (HB_DIRECTION_IS_HORIZONTAL (direction))
-    pos[j].y_offset = -pos[i].y_offset;
-  else
-    pos[j].x_offset = -pos[i].x_offset;
-
-  pos[j].attach_chain() = -chain;
-  pos[j].attach_type() = type;
-}
-static void
-propagate_attachment_offsets (hb_glyph_position_t *pos,
-			      unsigned int len,
-			      unsigned int i,
-			      hb_direction_t direction)
-{
-  /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate
-   * offset of glyph they are attached to. */
-  int chain = pos[i].attach_chain(), type = pos[i].attach_type();
-  if (likely (!chain))
-    return;
-
-  pos[i].attach_chain() = 0;
-
-  unsigned int j = (int) i + chain;
-
-  if (unlikely (j >= len))
-    return;
-
-  propagate_attachment_offsets (pos, len, j, direction);
-
-  assert (!!(type & ATTACH_TYPE_MARK) ^ !!(type & ATTACH_TYPE_CURSIVE));
-
-  if (type & ATTACH_TYPE_CURSIVE)
-  {
-    if (HB_DIRECTION_IS_HORIZONTAL (direction))
-      pos[i].y_offset += pos[j].y_offset;
-    else
-      pos[i].x_offset += pos[j].x_offset;
-  }
-  else /*if (type & ATTACH_TYPE_MARK)*/
-  {
-    pos[i].x_offset += pos[j].x_offset;
-    pos[i].y_offset += pos[j].y_offset;
-
-    assert (j < i);
-    if (HB_DIRECTION_IS_FORWARD (direction))
-      for (unsigned int k = j; k < i; k++) {
-	pos[i].x_offset -= pos[k].x_advance;
-	pos[i].y_offset -= pos[k].y_advance;
-      }
-    else
-      for (unsigned int k = j + 1; k < i + 1; k++) {
-	pos[i].x_offset += pos[k].x_advance;
-	pos[i].y_offset += pos[k].y_advance;
-      }
-  }
-}
-
-void
-GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
-{
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0;
-}
-
-void
-GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
-{
-  //_hb_buffer_assert_gsubgpos_vars (buffer);
-}
-
-void
-GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
-{
-  _hb_buffer_assert_gsubgpos_vars (buffer);
-
-  unsigned int len;
-  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
-  hb_direction_t direction = buffer->props.direction;
-
-  /* Handle attachments */
-  if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
-    for (unsigned i = 0; i < len; i++)
-      propagate_attachment_offsets (pos, len, i, direction);
-
-  if (unlikely (font->slant))
-  {
-    for (unsigned i = 0; i < len; i++)
-      if (unlikely (pos[i].y_offset))
-        pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset);
-  }
-}
+#include "OT/Layout/GPOS.hh"
 
+namespace OT {
 
-struct GPOS_accelerator_t : GPOS::accelerator_t {
-  GPOS_accelerator_t (hb_face_t *face) : GPOS::accelerator_t (face) {}
-};
+using Layout::GPOS_impl::PosLookup;
 
+namespace Layout {
+namespace GPOS_impl {
 
+// TODO(garretrieger): Move into new layout directory.
 /* Out-of-class implementation for methods recursing */
-
 #ifndef HB_NO_OT_LAYOUT
 template <typename context_t>
 /*static*/ typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
@@ -3121,13 +48,16 @@ template <typename context_t>
   return l.dispatch (c);
 }
 
-/*static*/ inline hb_closure_lookups_context_t::return_t PosLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
+template <>
+inline hb_closure_lookups_context_t::return_t
+PosLookup::dispatch_recurse_func<hb_closure_lookups_context_t> (hb_closure_lookups_context_t *c, unsigned this_index)
 {
   const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (this_index);
   return l.closure_lookups (c, this_index);
 }
 
-/*static*/ bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
+template <>
+inline bool PosLookup::dispatch_recurse_func<hb_ot_apply_context_t> (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
   const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
@@ -3141,8 +71,8 @@ template <typename context_t>
 }
 #endif
 
-
+} /* namespace GPOS_impl */
+} /* namespace Layout */
 } /* namespace OT */
 
-
 #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsub-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsub-table.hh
index bef381430b553a379192c1da40695ac585e45ea8..9e7f40c07bfa69936a42b63dbf9900b374ad9263 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsub-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsub-table.hh
@@ -36,6 +36,9 @@ namespace OT {
 using Layout::GSUB::SubstLookup;
 using Layout::GSUB::ExtensionSubst;
 
+namespace Layout {
+namespace GSUB {
+
 // TODO(garretrieger): Move into the new layout directory.
 /* Out-of-class implementation for methods recursing */
 
@@ -59,13 +62,16 @@ template <typename context_t>
   return l.dispatch (c);
 }
 
-/*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
+template <>
+inline hb_closure_lookups_context_t::return_t
+SubstLookup::dispatch_recurse_func<hb_closure_lookups_context_t> (hb_closure_lookups_context_t *c, unsigned this_index)
 {
   const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index);
   return l.closure_lookups (c, this_index);
 }
 
-/*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
+template <>
+inline bool SubstLookup::dispatch_recurse_func<hb_ot_apply_context_t> (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
   const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
@@ -79,8 +85,8 @@ template <typename context_t>
 }
 #endif
 
-
+} /* namespace GSUB */
+} /* namespace Layout */
 } /* namespace OT */
 
-
 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh
index 56e8c5b0063ce9082e6869183c75d67833fe3952..31da4986520807c0ce2c4b58553a97959fc0cdc6 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh
@@ -109,14 +109,10 @@ struct hb_closure_context_t :
     {
       done_lookups_glyph_count->set (lookup_index, glyphs->get_population ());
 
-      if (!done_lookups_glyph_set->get (lookup_index))
+      if (!done_lookups_glyph_set->has (lookup_index))
       {
-	hb_set_t* empty_set = hb_set_create ();
-	if (unlikely (!done_lookups_glyph_set->set (lookup_index, empty_set)))
-	{
-	  hb_set_destroy (empty_set);
+	if (unlikely (!done_lookups_glyph_set->set (lookup_index, hb::unique_ptr<hb_set_t> {hb_set_create ()})))
 	  return true;
-	}
       }
 
       hb_set_clear (done_lookups_glyph_set->get (lookup_index));
@@ -165,21 +161,19 @@ struct hb_closure_context_t :
   hb_set_t *glyphs;
   hb_set_t output[1];
   hb_vector_t<hb_set_t> active_glyphs_stack;
-  recurse_func_t recurse_func;
+  recurse_func_t recurse_func = nullptr;
   unsigned int nesting_level_left;
 
   hb_closure_context_t (hb_face_t *face_,
 			hb_set_t *glyphs_,
 			hb_map_t *done_lookups_glyph_count_,
-			hb_hashmap_t<unsigned, hb_set_t *> *done_lookups_glyph_set_,
+			hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *done_lookups_glyph_set_,
 			unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
 			  face (face_),
 			  glyphs (glyphs_),
-			  recurse_func (nullptr),
 			  nesting_level_left (nesting_level_left_),
 			  done_lookups_glyph_count (done_lookups_glyph_count_),
-			  done_lookups_glyph_set (done_lookups_glyph_set_),
-			  lookup_count (0)
+			  done_lookups_glyph_set (done_lookups_glyph_set_)
   {}
 
   ~hb_closure_context_t () { flush (); }
@@ -197,8 +191,8 @@ struct hb_closure_context_t :
 
   private:
   hb_map_t *done_lookups_glyph_count;
-  hb_hashmap_t<unsigned, hb_set_t *> *done_lookups_glyph_set;
-  unsigned int lookup_count;
+  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *done_lookups_glyph_set;
+  unsigned int lookup_count = 0;
 };
 
 
@@ -400,7 +394,6 @@ struct hb_collect_coverage_context_t :
   set_t *set;
 };
 
-
 struct hb_ot_apply_context_t :
        hb_dispatch_context_t<hb_ot_apply_context_t, bool, HB_DEBUG_APPLY>
 {
@@ -416,7 +409,7 @@ struct hb_ot_apply_context_t :
 	     match_func (nullptr),
 	     match_data (nullptr) {}
 
-    typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
+    typedef bool (*match_func_t) (hb_glyph_info_t &info, const HBUINT16 &value, const void *data);
 
     void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; }
     void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; }
@@ -434,7 +427,7 @@ struct hb_ot_apply_context_t :
       MATCH_MAYBE
     };
 
-    may_match_t may_match (const hb_glyph_info_t &info,
+    may_match_t may_match (hb_glyph_info_t &info,
 			   const HBUINT16        *glyph_data) const
     {
       if (!(info.mask & mask) ||
@@ -442,7 +435,7 @@ struct hb_ot_apply_context_t :
 	return MATCH_NO;
 
       if (match_func)
-	return match_func (info.codepoint, *glyph_data, match_data) ? MATCH_YES : MATCH_NO;
+	return match_func (info, *glyph_data, match_data) ? MATCH_YES : MATCH_NO;
 
       return MATCH_MAYBE;
     }
@@ -530,7 +523,7 @@ struct hb_ot_apply_context_t :
       while (idx + num_items < end)
       {
 	idx++;
-	const hb_glyph_info_t &info = c->buffer->info[idx];
+	hb_glyph_info_t &info = c->buffer->info[idx];
 
 	matcher_t::may_skip_t skip = matcher.may_skip (c, info);
 	if (unlikely (skip == matcher_t::SKIP_YES))
@@ -563,7 +556,7 @@ struct hb_ot_apply_context_t :
       while (idx > num_items - 1)
       {
 	idx--;
-	const hb_glyph_info_t &info = c->buffer->out_info[idx];
+	hb_glyph_info_t &info = c->buffer->out_info[idx];
 
 	matcher_t::may_skip_t skip = matcher.may_skip (c, info);
 	if (unlikely (skip == matcher_t::SKIP_YES))
@@ -611,7 +604,10 @@ struct hb_ot_apply_context_t :
   return_t recurse (unsigned int sub_lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func || buffer->max_ops-- <= 0))
+    {
+      buffer->shaping_failed = true;
       return default_return_value ();
+    }
 
     nesting_level_left--;
     bool ret = recurse_func (this, sub_lookup_index);
@@ -621,35 +617,34 @@ struct hb_ot_apply_context_t :
 
   skipping_iterator_t iter_input, iter_context;
 
+  unsigned int table_index; /* GSUB/GPOS */
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
-  recurse_func_t recurse_func;
+  recurse_func_t recurse_func = nullptr;
   const GDEF &gdef;
   const VariationStore &var_store;
+  VariationStore::cache_t *var_store_cache;
 
   hb_direction_t direction;
-  hb_mask_t lookup_mask;
-  unsigned int table_index; /* GSUB/GPOS */
-  unsigned int lookup_index;
-  unsigned int lookup_props;
-  unsigned int nesting_level_left;
+  hb_mask_t lookup_mask = 1;
+  unsigned int lookup_index = (unsigned) -1;
+  unsigned int lookup_props = 0;
+  unsigned int nesting_level_left = HB_MAX_NESTING_LEVEL;
 
   bool has_glyph_classes;
-  bool auto_zwnj;
-  bool auto_zwj;
-  bool per_syllable;
-  bool random;
-
-  uint32_t random_state;
-
+  bool auto_zwnj = true;
+  bool auto_zwj = true;
+  bool per_syllable = false;
+  bool random = false;
+  uint32_t random_state = 1;
+  unsigned new_syllables = (unsigned) -1;
 
   hb_ot_apply_context_t (unsigned int table_index_,
 			 hb_font_t *font_,
 			 hb_buffer_t *buffer_) :
-			iter_input (), iter_context (),
+			table_index (table_index_),
 			font (font_), face (font->face), buffer (buffer_),
-			recurse_func (nullptr),
 			gdef (
 #ifndef HB_NO_OT_LAYOUT
 			      *face->table.GDEF->table
@@ -658,18 +653,23 @@ struct hb_ot_apply_context_t :
 #endif
 			     ),
 			var_store (gdef.get_var_store ()),
+			var_store_cache (
+#ifndef HB_NO_VAR
+					 table_index == 1 && font->num_coords ? var_store.create_cache () : nullptr
+#else
+					 nullptr
+#endif
+					),
 			direction (buffer_->props.direction),
-			lookup_mask (1),
-			table_index (table_index_),
-			lookup_index ((unsigned int) -1),
-			lookup_props (0),
-			nesting_level_left (HB_MAX_NESTING_LEVEL),
-			has_glyph_classes (gdef.has_glyph_classes ()),
-			auto_zwnj (true),
-			auto_zwj (true),
-			per_syllable (false),
-			random (false),
-			random_state (1) { init_iters (); }
+			has_glyph_classes (gdef.has_glyph_classes ())
+  { init_iters (); }
+
+  ~hb_ot_apply_context_t ()
+  {
+#ifndef HB_NO_VAR
+    VariationStore::destroy_cache (var_store_cache);
+#endif
+  }
 
   void init_iters ()
   {
@@ -736,6 +736,9 @@ struct hb_ot_apply_context_t :
 			  bool ligature = false,
 			  bool component = false) const
   {
+    if (new_syllables != (unsigned) -1)
+      buffer->cur().syllable() = new_syllables;
+
     unsigned int props = _hb_glyph_info_get_glyph_props (&buffer->cur());
     props |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED;
     if (ligature)
@@ -790,8 +793,8 @@ struct hb_ot_apply_context_t :
 };
 
 
-struct hb_get_subtables_context_t :
-       hb_dispatch_context_t<hb_get_subtables_context_t>
+struct hb_accelerate_subtables_context_t :
+       hb_dispatch_context_t<hb_accelerate_subtables_context_t>
 {
   template <typename Type>
   static inline bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c)
@@ -800,15 +803,53 @@ struct hb_get_subtables_context_t :
     return typed_obj->apply (c);
   }
 
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+  template <typename T>
+  static inline auto apply_cached_ (const T *obj, OT::hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (bool, obj->apply (c, true) )
+  template <typename T>
+  static inline auto apply_cached_ (const T *obj, OT::hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (bool, obj->apply (c) )
+  template <typename Type>
+  static inline bool apply_cached_to (const void *obj, OT::hb_ot_apply_context_t *c)
+  {
+    const Type *typed_obj = (const Type *) obj;
+    return apply_cached_ (typed_obj, c, hb_prioritize);
+  }
+
+  template <typename T>
+  static inline auto cache_func_ (const T *obj, OT::hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) )
+  template <typename T>
+  static inline bool cache_func_ (const T *obj, OT::hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; }
+  template <typename Type>
+  static inline bool cache_func_to (const void *obj, OT::hb_ot_apply_context_t *c, bool enter)
+  {
+    const Type *typed_obj = (const Type *) obj;
+    return cache_func_ (typed_obj, c, enter, hb_prioritize);
+  }
+#endif
+
   typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_ot_apply_context_t *c);
+  typedef bool (*hb_cache_func_t) (const void *obj, OT::hb_ot_apply_context_t *c, bool enter);
 
   struct hb_applicable_t
   {
+    friend struct hb_accelerate_subtables_context_t;
+    friend struct hb_ot_layout_lookup_accelerator_t;
+
     template <typename T>
-    void init (const T &obj_, hb_apply_func_t apply_func_)
+    void init (const T &obj_,
+	       hb_apply_func_t apply_func_
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+	       , hb_apply_func_t apply_cached_func_
+	       , hb_cache_func_t cache_func_
+#endif
+		)
     {
       obj = &obj_;
       apply_func = apply_func_;
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+      apply_cached_func = apply_cached_func_;
+      cache_func = cache_func_;
+#endif
       digest.init ();
       obj_.get_coverage ().collect_coverage (&digest);
     }
@@ -817,38 +858,93 @@ struct hb_get_subtables_context_t :
     {
       return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c);
     }
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    bool apply_cached (OT::hb_ot_apply_context_t *c) const
+    {
+      return digest.may_have (c->buffer->cur().codepoint) &&  apply_cached_func (obj, c);
+    }
+    bool cache_enter (OT::hb_ot_apply_context_t *c) const
+    {
+      return cache_func (obj, c, true);
+    }
+    void cache_leave (OT::hb_ot_apply_context_t *c) const
+    {
+      cache_func (obj, c, false);
+    }
+#endif
 
     private:
     const void *obj;
     hb_apply_func_t apply_func;
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    hb_apply_func_t apply_cached_func;
+    hb_cache_func_t cache_func;
+#endif
     hb_set_digest_t digest;
   };
 
   typedef hb_vector_t<hb_applicable_t> array_t;
 
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+  template <typename T>
+  auto cache_cost (const T &obj, hb_priority<1>) HB_AUTO_RETURN ( obj.cache_cost () )
+  template <typename T>
+  auto cache_cost (const T &obj, hb_priority<0>) HB_AUTO_RETURN ( 0u )
+#endif
+
   /* Dispatch interface. */
   template <typename T>
   return_t dispatch (const T &obj)
   {
-    hb_applicable_t *entry = array.push();
-    entry->init (obj, apply_to<T>);
+    hb_applicable_t entry;
+
+    entry.init (obj,
+		apply_to<T>
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+		, apply_cached_to<T>
+		, cache_func_to<T>
+#endif
+		);
+
+    array.push (entry);
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    /* Cache handling
+     *
+     * We allow one subtable from each lookup to use a cache. The assumption
+     * being that multiple subtables of the same lookup cannot use a cache
+     * because the resources they would use will collide.  As such, we ask
+     * each subtable to tell us how much it costs (which a cache would avoid),
+     * and we allocate the cache opportunity to the costliest subtable.
+     */
+    unsigned cost = cache_cost (obj, hb_prioritize);
+    if (cost > cache_user_cost && !array.in_error ())
+    {
+      cache_user_idx = array.length - 1;
+      cache_user_cost = cost;
+    }
+#endif
+
     return hb_empty_t ();
   }
   static return_t default_return_value () { return hb_empty_t (); }
 
-  hb_get_subtables_context_t (array_t &array_) :
-			      array (array_) {}
+  hb_accelerate_subtables_context_t (array_t &array_) :
+				     array (array_) {}
 
   array_t &array;
-};
-
 
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+  unsigned cache_user_idx = (unsigned) -1;
+  unsigned cache_user_cost = 0;
+#endif
+};
 
 
 typedef bool (*intersects_func_t) (const hb_set_t *glyphs, const HBUINT16 &value, const void *data);
 typedef void (*intersected_glyphs_func_t) (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs);
 typedef void (*collect_glyphs_func_t) (hb_set_t *glyphs, const HBUINT16 &value, const void *data);
-typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
+typedef bool (*match_func_t) (hb_glyph_info_t &info, const HBUINT16 &value, const void *data);
 
 struct ContextClosureFuncs
 {
@@ -863,6 +959,10 @@ struct ContextApplyFuncs
 {
   match_func_t match;
 };
+struct ChainContextApplyFuncs
+{
+  match_func_t match[3];
+};
 
 
 static inline bool intersects_glyph (const hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
@@ -939,19 +1039,30 @@ static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED,
 }
 
 
-static inline bool match_glyph (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data HB_UNUSED)
+static inline bool match_glyph (hb_glyph_info_t &info, const HBUINT16 &value, const void *data HB_UNUSED)
 {
-  return glyph_id == value;
+  return info.codepoint == value;
 }
-static inline bool match_class (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
+static inline bool match_class (hb_glyph_info_t &info, const HBUINT16 &value, const void *data)
 {
   const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
-  return class_def.get_class (glyph_id) == value;
+  return class_def.get_class (info.codepoint) == value;
 }
-static inline bool match_coverage (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
+static inline bool match_class_cached (hb_glyph_info_t &info, const HBUINT16 &value, const void *data)
+{
+  unsigned klass = info.syllable();
+  if (klass < 255)
+    return klass == value;
+  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+  klass = class_def.get_class (info.codepoint);
+  if (likely (klass < 255))
+    info.syllable() = klass;
+  return klass == value;
+}
+static inline bool match_coverage (hb_glyph_info_t &info, const HBUINT16 &value, const void *data)
 {
   const Offset16To<Coverage> &coverage = (const Offset16To<Coverage>&)value;
-  return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
+  return (data+coverage).get_coverage (info.codepoint) != NOT_COVERED;
 }
 
 static inline bool would_match_input (hb_would_apply_context_t *c,
@@ -964,8 +1075,12 @@ static inline bool would_match_input (hb_would_apply_context_t *c,
     return false;
 
   for (unsigned int i = 1; i < count; i++)
-    if (likely (!match_func (c->glyphs[i], input[i - 1], match_data)))
+  {
+    hb_glyph_info_t info;
+    info.codepoint = c->glyphs[i];
+    if (likely (!match_func (info, input[i - 1], match_data)))
       return false;
+  }
 
   return true;
 }
@@ -1328,7 +1443,7 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c,
     bool has_pos_glyphs = false;
     hb_set_t pos_glyphs;
 
-    if (hb_set_is_empty (covered_seq_indicies) || !hb_set_has (covered_seq_indicies, seqIndex))
+    if (!hb_set_has (covered_seq_indicies, seqIndex))
     {
       has_pos_glyphs = true;
       if (seqIndex == 0)
@@ -1361,7 +1476,7 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c,
 
     covered_seq_indicies->add (seqIndex);
     if (has_pos_glyphs) {
-      c->push_cur_active_glyphs () = pos_glyphs;
+      c->push_cur_active_glyphs () = std::move (pos_glyphs);
     } else {
       c->push_cur_active_glyphs ().set (*c->glyphs);
     }
@@ -2125,19 +2240,54 @@ struct ContextFormat2
 
   const Coverage &get_coverage () const { return this+coverage; }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
+  {
+    unsigned c = (this+classDef).cost () * ruleSet.len;
+    return c >= 4 ? c : 0;
+  }
+  bool cache_func (hb_ot_apply_context_t *c, bool enter) const
+  {
+    if (enter)
+    {
+      if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
+	return false;
+      auto &info = c->buffer->info;
+      unsigned count = c->buffer->len;
+      for (unsigned i = 0; i < count; i++)
+	info[i].syllable() = 255;
+      c->new_syllables = 255;
+      return true;
+    }
+    else
+    {
+      c->new_syllables = (unsigned) -1;
+      HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
+      return true;
+    }
+  }
+
+  bool apply (hb_ot_apply_context_t *c, bool cached = false) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     const ClassDef &class_def = this+classDef;
-    index = class_def.get_class (c->buffer->cur().codepoint);
-    const RuleSet &rule_set = this+ruleSet[index];
+
     struct ContextApplyLookupContext lookup_context = {
-      {match_class},
+      {cached ? match_class_cached : match_class},
       &class_def
     };
+
+    if (cached && c->buffer->cur().syllable() < 255)
+      index = c->buffer->cur().syllable ();
+    else
+    {
+      index = class_def.get_class (c->buffer->cur().codepoint);
+      if (cached && index < 255)
+	c->buffer->cur().syllable() = index;
+    }
+    const RuleSet &rule_set = this+ruleSet[index];
     return_trace (rule_set.apply (c, lookup_context));
   }
 
@@ -2411,7 +2561,7 @@ struct ChainContextCollectGlyphsLookupContext
 
 struct ChainContextApplyLookupContext
 {
-  ContextApplyFuncs funcs;
+  ChainContextApplyFuncs funcs;
   const void *match_data[3];
 };
 
@@ -2499,7 +2649,7 @@ static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c
   return (c->zero_context ? !backtrackCount && !lookaheadCount : true)
       && would_match_input (c,
 			    inputCount, input,
-			    lookup_context.funcs.match, lookup_context.match_data[1]);
+			    lookup_context.funcs.match[1], lookup_context.match_data[1]);
 }
 
 static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
@@ -2518,11 +2668,11 @@ static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
   unsigned match_positions[HB_MAX_CONTEXT_LENGTH];
   if (!(match_input (c,
 		     inputCount, input,
-		     lookup_context.funcs.match, lookup_context.match_data[1],
+		     lookup_context.funcs.match[1], lookup_context.match_data[1],
 		     &match_end, match_positions) && (end_index = match_end)
        && match_lookahead (c,
 			   lookaheadCount, lookahead,
-			   lookup_context.funcs.match, lookup_context.match_data[2],
+			   lookup_context.funcs.match[2], lookup_context.match_data[2],
 			   match_end, &end_index)))
   {
     c->buffer->unsafe_to_concat (c->buffer->idx, end_index);
@@ -2532,7 +2682,7 @@ static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
   unsigned start_index = c->buffer->out_len;
   if (!match_backtrack (c,
 			backtrackCount, backtrack,
-			lookup_context.funcs.match, lookup_context.match_data[0],
+			lookup_context.funcs.match[0], lookup_context.match_data[0],
 			&start_index))
   {
     c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
@@ -2934,7 +3084,7 @@ struct ChainContextFormat1
   {
     const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_glyph},
+      {{match_glyph, match_glyph, match_glyph}},
       {nullptr, nullptr, nullptr}
     };
     return rule_set.would_apply (c, lookup_context);
@@ -2950,7 +3100,7 @@ struct ChainContextFormat1
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_glyph},
+      {{match_glyph, match_glyph, match_glyph}},
       {nullptr, nullptr, nullptr}
     };
     return_trace (rule_set.apply (c, lookup_context));
@@ -3134,7 +3284,7 @@ struct ChainContextFormat2
     unsigned int index = input_class_def.get_class (c->glyphs[0]);
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_class},
+      {{match_class, match_class, match_class}},
       {&backtrack_class_def,
        &input_class_def,
        &lookahead_class_def}
@@ -3144,7 +3294,33 @@ struct ChainContextFormat2
 
   const Coverage &get_coverage () const { return this+coverage; }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
+  {
+    unsigned c = (this+lookaheadClassDef).cost () * ruleSet.len;
+    return c >= 4 ? c : 0;
+  }
+  bool cache_func (hb_ot_apply_context_t *c, bool enter) const
+  {
+    if (enter)
+    {
+      if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
+	return false;
+      auto &info = c->buffer->info;
+      unsigned count = c->buffer->len;
+      for (unsigned i = 0; i < count; i++)
+	info[i].syllable() = 255;
+      c->new_syllables = 255;
+      return true;
+    }
+    else
+    {
+      c->new_syllables = (unsigned) -1;
+      HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
+      return true;
+    }
+  }
+
+  bool apply (hb_ot_apply_context_t *c, bool cached = false) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -3154,14 +3330,27 @@ struct ChainContextFormat2
     const ClassDef &input_class_def = this+inputClassDef;
     const ClassDef &lookahead_class_def = this+lookaheadClassDef;
 
-    index = input_class_def.get_class (c->buffer->cur().codepoint);
-    const ChainRuleSet &rule_set = this+ruleSet[index];
+    /* For ChainContextFormat2 we cache the LookaheadClassDef instead of InputClassDef.
+     * The reason is that most heavy fonts want to identify a glyph in context and apply
+     * a lookup to it. In this scenario, the length of the input sequence is one, whereas
+     * the lookahead / backtrack are typically longer.  The one glyph in input sequence is
+     * looked-up below and no input glyph is looked up in individual rules, whereas the
+     * lookahead and backtrack glyphs are tried.  Since we match lookahead before backtrack,
+     * we should cache lookahead.  This decisions showed a 20% improvement in shaping of
+     * the Gulzar font.
+     */
+
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_class},
+      {{cached && &backtrack_class_def == &input_class_def ? match_class_cached : match_class,
+        cached && &input_class_def == &lookahead_class_def ? match_class_cached : match_class,
+        cached ? match_class_cached : match_class}},
       {&backtrack_class_def,
        &input_class_def,
        &lookahead_class_def}
     };
+
+    index = input_class_def.get_class (c->buffer->cur().codepoint);
+    const ChainRuleSet &rule_set = this+ruleSet[index];
     return_trace (rule_set.apply (c, lookup_context));
   }
 
@@ -3359,7 +3548,7 @@ struct ChainContextFormat3
     const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (input);
     const Array16Of<LookupRecord> &lookup = StructAfter<Array16Of<LookupRecord>> (lookahead);
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_coverage},
+      {{match_coverage, match_coverage, match_coverage}},
       {this, this, this}
     };
     return chain_context_would_apply_lookup (c,
@@ -3386,7 +3575,7 @@ struct ChainContextFormat3
     const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (input);
     const Array16Of<LookupRecord> &lookup = StructAfter<Array16Of<LookupRecord>> (lookahead);
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_coverage},
+      {{match_coverage, match_coverage, match_coverage}},
       {this, this, this}
     };
     return_trace (chain_context_apply_lookup (c,
@@ -3623,25 +3812,63 @@ struct hb_ot_layout_lookup_accelerator_t
     lookup.collect_coverage (&digest);
 
     subtables.init ();
-    OT::hb_get_subtables_context_t c_get_subtables (subtables);
-    lookup.dispatch (&c_get_subtables);
+    OT::hb_accelerate_subtables_context_t c_accelerate_subtables (subtables);
+    lookup.dispatch (&c_accelerate_subtables);
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    cache_user_idx = c_accelerate_subtables.cache_user_idx;
+    for (unsigned i = 0; i < subtables.length; i++)
+      if (i != cache_user_idx)
+	subtables[i].apply_cached_func = subtables[i].apply_func;
+#endif
   }
   void fini () { subtables.fini (); }
 
   bool may_have (hb_codepoint_t g) const
   { return digest.may_have (g); }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c, bool use_cache) const
   {
-    for (unsigned int i = 0; i < subtables.length; i++)
-      if (subtables[i].apply (c))
-	return true;
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    if (use_cache)
+    {
+      for (unsigned int i = 0; i < subtables.length; i++)
+        if (subtables[i].apply_cached (c))
+	  return true;
+    }
+    else
+#endif
+    {
+      for (unsigned int i = 0; i < subtables.length; i++)
+        if (subtables[i].apply (c))
+	  return true;
+    }
     return false;
   }
 
+  bool cache_enter (OT::hb_ot_apply_context_t *c) const
+  {
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    return cache_user_idx != (unsigned) -1 &&
+	   subtables[cache_user_idx].cache_enter (c);
+#else
+    return false;
+#endif
+  }
+  void cache_leave (OT::hb_ot_apply_context_t *c) const
+  {
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    subtables[cache_user_idx].cache_leave (c);
+#endif
+  }
+
+
   private:
   hb_set_digest_t digest;
-  hb_get_subtables_context_t::array_t subtables;
+  hb_accelerate_subtables_context_t::array_t subtables;
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+  unsigned cache_user_idx = (unsigned) -1;
+#endif
 };
 
 struct GSUBGPOS
@@ -3721,6 +3948,8 @@ struct GSUBGPOS
     hb_set_t visited_lookups, inactive_lookups;
     OT::hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups);
 
+    c.set_recurse_func (TLookup::template dispatch_recurse_func<hb_closure_lookups_context_t>);
+
     for (unsigned lookup_index : + hb_iter (lookup_indexes))
       reinterpret_cast<const TLookup &> (get_lookup (lookup_index)).closure_lookups (&c, lookup_index);
 
@@ -3729,7 +3958,7 @@ struct GSUBGPOS
   }
 
   void prune_langsys (const hb_map_t *duplicate_feature_map,
-                      hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map,
+                      hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map,
                       hb_set_t       *new_feature_indexes /* OUT */) const
   {
     hb_prune_langsys_context_t c (this, script_langsys_map, duplicate_feature_map, new_feature_indexes);
@@ -3787,7 +4016,7 @@ struct GSUBGPOS
                                 hb_map_t *duplicate_feature_map /* OUT */) const
   {
     if (feature_indices->is_empty ()) return;
-    hb_hashmap_t<hb_tag_t, hb_set_t *> unique_features;
+    hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features;
     //find out duplicate features after subset
     for (unsigned i : feature_indices->iter ())
     {
@@ -3795,16 +4024,9 @@ struct GSUBGPOS
       if (t == HB_MAP_VALUE_INVALID) continue;
       if (!unique_features.has (t))
       {
-        hb_set_t* indices = hb_set_create ();
-        if (unlikely (indices == hb_set_get_empty () ||
-                      !unique_features.set (t, indices)))
-        {
-          hb_set_destroy (indices);
-          for (auto _ : unique_features.iter ())
-            hb_set_destroy (_.second);
+        if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()})))
           return;
-        }
-        if (unique_features.get (t))
+        if (unique_features.has (t))
           unique_features.get (t)->add (i);
         duplicate_feature_map->set (i, i);
         continue;
@@ -3849,9 +4071,6 @@ struct GSUBGPOS
         duplicate_feature_map->set (i, i);
       }
     }
-
-    for (auto _ : unique_features.iter ())
-      hb_set_destroy (_.second);
   }
 
   void prune_features (const hb_map_t *lookup_indices, /* IN */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.cc
index f4ea21a4f9c85130fe36975fcd14d2e36cb1a871..142c843dad8a7432ed832a2037cb9514a1778815 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.cc
@@ -46,7 +46,7 @@
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
-#include "hb-ot-layout-base-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-layout-base-table.hh"
 #include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-ot-name-table.hh"
 #include "hb-ot-os2-table.hh"
@@ -55,6 +55,7 @@
 #include "hb-aat-layout-opbd-table.hh" // Just so we compile it; unused otherwise.
 
 using OT::Layout::GSUB::GSUB;
+using OT::Layout::GPOS;
 
 /**
  * SECTION:hb-ot-layout
@@ -260,7 +261,6 @@ _hb_ot_layout_set_glyph_props (hb_font_t *font,
   {
     _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
     _hb_glyph_info_clear_lig_props (&buffer->info[i]);
-    buffer->info[i].syllable() = 0;
   }
 }
 
@@ -401,7 +401,7 @@ GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED,
 }
 
 bool
-OT::GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED,
+GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED,
 			  hb_face_t *face HB_UNUSED) const
 {
 #ifdef HB_NO_OT_LAYOUT_BLOCKLIST
@@ -1501,15 +1501,12 @@ hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 					hb_set_t     *glyphs /* OUT */)
 {
   hb_map_t done_lookups_glyph_count;
-  hb_hashmap_t<unsigned, hb_set_t *> done_lookups_glyph_set;
+  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set;
   OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
 
   const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
 
   l.closure (&c, lookup_index);
-
-  for (auto _ : done_lookups_glyph_set.iter ())
-    hb_set_destroy (_.second);
 }
 
 /**
@@ -1529,7 +1526,7 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t      *face,
 					 hb_set_t       *glyphs /* OUT */)
 {
   hb_map_t done_lookups_glyph_count;
-  hb_hashmap_t<unsigned, hb_set_t *> done_lookups_glyph_set;
+  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set;
   OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
   const GSUB& gsub = *face->table.GSUB->table;
 
@@ -1551,13 +1548,10 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t      *face,
     }
   } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES &&
 	   glyphs_length != glyphs->get_population ());
-
-  for (auto _ : done_lookups_glyph_set.iter ())
-    hb_set_destroy (_.second);
 }
 
 /*
- * OT::GPOS
+ * GPOS
  */
 
 
@@ -1588,7 +1582,7 @@ hb_ot_layout_has_positioning (hb_face_t *face)
 void
 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 {
-  OT::GPOS::position_start (font, buffer);
+  GPOS::position_start (font, buffer);
 }
 
 
@@ -1603,7 +1597,7 @@ hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 void
 hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
 {
-  OT::GPOS::position_finish_advances (font, buffer);
+  GPOS::position_finish_advances (font, buffer);
 }
 
 /**
@@ -1617,7 +1611,7 @@ hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
 void
 hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
 {
-  OT::GPOS::position_finish_offsets (font, buffer);
+  GPOS::position_finish_offsets (font, buffer);
 }
 
 
@@ -1652,7 +1646,7 @@ hb_ot_layout_get_size_params (hb_face_t       *face,
 			      unsigned int    *range_start,       /* OUT.  May be NULL */
 			      unsigned int    *range_end          /* OUT.  May be NULL */)
 {
-  const OT::GPOS &gpos = *face->table.GPOS->table;
+  const GPOS &gpos = *face->table.GPOS->table;
   const hb_tag_t tag = HB_TAG ('s','i','z','e');
 
   unsigned int num_features = gpos.get_feature_count ();
@@ -1803,7 +1797,7 @@ hb_ot_layout_feature_get_characters (hb_face_t      *face,
 struct GSUBProxy
 {
   static constexpr unsigned table_index = 0u;
-  static constexpr bool inplace = false;
+  static constexpr bool always_inplace = false;
   typedef OT::SubstLookup Lookup;
 
   GSUBProxy (hb_face_t *face) :
@@ -1817,14 +1811,14 @@ struct GSUBProxy
 struct GPOSProxy
 {
   static constexpr unsigned table_index = 1u;
-  static constexpr bool inplace = true;
+  static constexpr bool always_inplace = true;
   typedef OT::PosLookup Lookup;
 
   GPOSProxy (hb_face_t *face) :
     table (*face->table.GPOS->table),
     accels (face->table.GPOS->accels) {}
 
-  const OT::GPOS &table;
+  const GPOS &table;
   const OT::hb_ot_layout_lookup_accelerator_t *accels;
 };
 
@@ -1833,6 +1827,8 @@ static inline bool
 apply_forward (OT::hb_ot_apply_context_t *c,
 	       const OT::hb_ot_layout_lookup_accelerator_t &accel)
 {
+  bool use_cache = accel.cache_enter (c);
+
   bool ret = false;
   hb_buffer_t *buffer = c->buffer;
   while (buffer->idx < buffer->len && buffer->successful)
@@ -1842,7 +1838,7 @@ apply_forward (OT::hb_ot_apply_context_t *c,
 	(buffer->cur().mask & c->lookup_mask) &&
 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
      {
-       applied = accel.apply (c);
+       applied = accel.apply (c, use_cache);
      }
 
     if (applied)
@@ -1850,6 +1846,10 @@ apply_forward (OT::hb_ot_apply_context_t *c,
     else
       (void) buffer->next_glyph ();
   }
+
+  if (use_cache)
+    accel.cache_leave (c);
+
   return ret;
 }
 
@@ -1864,7 +1864,7 @@ apply_backward (OT::hb_ot_apply_context_t *c,
     if (accel.may_have (buffer->cur().codepoint) &&
 	(buffer->cur().mask & c->lookup_mask) &&
 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
-     ret |= accel.apply (c);
+     ret |= accel.apply (c, false);
 
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
     buffer->idx--;
@@ -1890,13 +1890,13 @@ apply_string (OT::hb_ot_apply_context_t *c,
   if (likely (!lookup.is_reverse ()))
   {
     /* in/out forward substitution/positioning */
-    if (!Proxy::inplace)
+    if (!Proxy::always_inplace)
       buffer->clear_output ();
 
     buffer->idx = 0;
     apply_forward (c, accel);
 
-    if (!Proxy::inplace)
+    if (!Proxy::always_inplace)
       buffer->sync ();
   }
   else
@@ -1917,7 +1917,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
   const unsigned int table_index = proxy.table_index;
   unsigned int i = 0;
   OT::hb_ot_apply_context_t c (table_index, font, buffer);
-  c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
+  c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
 
   for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
   {
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.hh
index 75bba0bc506154d0bc90844b225c623791e51009..6395f06670674fb8b59b940df4a8eff06304ecfd 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.hh
@@ -589,13 +589,11 @@ _hb_buffer_allocate_gsubgpos_vars (hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props);
   HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
-  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
 }
 
 static inline void
 _hb_buffer_deallocate_gsubgpos_vars (hb_buffer_t *buffer)
 {
-  HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
   HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_props);
 }
@@ -605,7 +603,6 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer)
 {
   HB_BUFFER_ASSERT_VAR (buffer, glyph_props);
   HB_BUFFER_ASSERT_VAR (buffer, lig_props);
-  HB_BUFFER_ASSERT_VAR (buffer, syllable);
 }
 
 /* Make sure no one directly touches our props... */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-language-static.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-language-static.hh
index c496dc2981a374a34688e1ff71e115390ee7dca6..0e0f2d632af246d17bde3598e13207b200743d51 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-language-static.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-language-static.hh
@@ -45,7 +45,7 @@ struct hb_ot_language_map_t
 };
 
 static const hb_ot_language_map_t
-hb_ms_language_map[] =
+_hb_ms_language_map[] =
 {
   {0x0001,	"ar"},	/* ??? */
   {0x0004,	"zh"},	/* ??? */
@@ -298,7 +298,7 @@ hb_ms_language_map[] =
 };
 
 static const hb_ot_language_map_t
-hb_mac_language_map[] =
+_hb_mac_language_map[] =
 {
   {  0,	"en"},	/* English */
   {  1,	"fr"},	/* French */
@@ -441,16 +441,16 @@ hb_language_t
 _hb_ot_name_language_for_ms_code (unsigned int code)
 {
   return _hb_ot_name_language_for (code,
-				   hb_ms_language_map,
-				   ARRAY_LENGTH (hb_ms_language_map));
+				   _hb_ms_language_map,
+				   ARRAY_LENGTH (_hb_ms_language_map));
 }
 
 hb_language_t
 _hb_ot_name_language_for_mac_code (unsigned int code)
 {
   return _hb_ot_name_language_for (code,
-				   hb_mac_language_map,
-				   ARRAY_LENGTH (hb_mac_language_map));
+				   _hb_mac_language_map,
+				   ARRAY_LENGTH (_hb_mac_language_map));
 }
 
 #endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-table.hh
index d52367e9b1c3e53106757bca3400b451e9b48c19..01107aad67bb62645865034b3faa047ec8b76099 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-name-table.hh
@@ -156,7 +156,7 @@ struct NameRecord
 };
 
 static int
-_hb_ot_name_entry_cmp_key (const void *pa, const void *pb)
+_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact)
 {
   const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
   const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
@@ -169,8 +169,23 @@ _hb_ot_name_entry_cmp_key (const void *pa, const void *pb)
   if (a->language == b->language) return 0;
   if (!a->language) return -1;
   if (!b->language) return +1;
-  return strcmp (hb_language_to_string (a->language),
-		 hb_language_to_string (b->language));
+
+  const char *astr = hb_language_to_string (a->language);
+  const char *bstr = hb_language_to_string (b->language);
+
+  signed c = strcmp (astr, bstr);
+
+  if (!exact && c)
+  {
+    unsigned la = strlen (astr);
+    unsigned lb = strlen (bstr);
+    // 'a' is the user request, and 'b' is string in the font.
+    // If eg. user asks for "en-us" and font has "en", approve.
+    if (la > lb && astr[lb] == '-' && !strncmp (astr, bstr, lb))
+      return 0;
+  }
+
+  return c;
 }
 
 static int
@@ -178,7 +193,7 @@ _hb_ot_name_entry_cmp (const void *pa, const void *pb)
 {
   /* Compare by name_id, then language, then score, then index. */
 
-  int v = _hb_ot_name_entry_cmp_key (pa, pb);
+  int v = _hb_ot_name_entry_cmp_key (pa, pb, true);
   if (v)
     return v;
 
@@ -330,7 +345,18 @@ struct name
       const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
 						    this->names.length,
 						    sizeof (hb_ot_name_entry_t),
-						    _hb_ot_name_entry_cmp_key);
+						    _hb_ot_name_entry_cmp_key,
+						    true);
+
+      if (!entry)
+      {
+	entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
+			    this->names.length,
+			    sizeof (hb_ot_name_entry_t),
+			    _hb_ot_name_entry_cmp_key,
+			    false);
+      }
+
       if (!entry)
 	return -1;
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-os2-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-os2-table.hh
index f0035e2f04efe45a2f949fcf61dbbb665f8f3ae9..3473afef5465e90981b11b964b5adfd6a095c978 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-os2-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-os2-table.hh
@@ -224,9 +224,11 @@ struct OS2
     *max_cp = hb_min (0xFFFFu, codepoints->get_max ());
   }
 
-  /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 */
+  /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681
+   * https://docs.microsoft.com/en-us/typography/legacy/legacy_arabic_fonts */
   enum font_page_t
   {
+    FONT_PAGE_NONE		= 0,
     FONT_PAGE_HEBREW		= 0xB100, /* Hebrew Windows 3.1 font page */
     FONT_PAGE_SIMP_ARABIC	= 0xB200, /* Simplified Arabic Windows 3.1 font page */
     FONT_PAGE_TRAD_ARABIC	= 0xB300, /* Traditional Arabic Windows 3.1 font page */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-post-table-v2subset.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-post-table-v2subset.hh
index 0f3cd8e24fe2b88ddda55db15f5148ae5fed6e44..4d427e543129b5a62500e2d37dbab0e777cb4597 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-post-table-v2subset.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-post-table-v2subset.hh
@@ -52,16 +52,16 @@ HB_INTERNAL bool postV2Tail::serialize (hb_serialize_context_t *c,
   {
     unsigned glyph_id = _.first;
     unsigned new_index = _.second;
-    
+
     if (new_index < 258) continue;
     if (copied_indices.has (new_index)) continue;
     copied_indices.add (new_index);
-    
+
     hb_bytes_t s = reinterpret_cast<const post::accelerator_t*> (_post)->find_glyph_name (glyph_id);
     HBUINT8 *o = c->allocate_size<HBUINT8> (HBUINT8::static_size * (s.length + 1));
     if (unlikely (!o)) return_trace (false);
     if (!c->check_assign (o[0], s.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
-    memcpy (o+1, s.arrayZ, HBUINT8::static_size * s.length);
+    hb_memcpy (o+1, s.arrayZ, HBUINT8::static_size * s.length);
   }
 
   return_trace (true);
@@ -78,17 +78,19 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const
 
   post::accelerator_t _post (c->plan->source);
 
-  hb_hashmap_t<hb_bytes_t, unsigned, std::nullptr_t, unsigned, nullptr, (unsigned)-1> glyph_name_to_new_index;
+  hb_hashmap_t<hb_bytes_t, unsigned, true> glyph_name_to_new_index;
   for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
   {
     hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
     unsigned old_index = glyphNameIndex[old_gid];
 
     unsigned new_index;
+    const unsigned *new_index2;
     if (old_index <= 257) new_index = old_index;
-    else if (old_new_index_map.has (old_index)) new_index = old_new_index_map.get (old_index);
-    else
+    else if (old_new_index_map.has (old_index, &new_index2))
     {
+      new_index = *new_index2;
+    } else {
       hb_bytes_t s = _post.find_glyph_name (old_gid);
       new_index = glyph_name_to_new_index.get (s);
       if (new_index == (unsigned)-1)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-machine.hh
deleted file mode 100644
index 74bf3ca0faea6a6370e0c2b0d565b92f482bba57..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-machine.hh
+++ /dev/null
@@ -1,603 +0,0 @@
-
-#line 1 "hb-ot-shape-complex-indic-machine.rl"
-/*
- * Copyright © 2011,2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
-
-#include "hb.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 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, 
-	4u, 8u, 4u, 13u, 4u, 13u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 
-	6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u, 
-	4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 5u, 8u, 8u, 8u, 1u, 19u, 
-	3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u, 
-	1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 
-	3u, 10u, 5u, 10u, 3u, 17u, 3u, 17u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 
-	3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 
-	1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 
-	5u, 10u, 3u, 17u, 3u, 17u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 
-	3u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u, 
-	4u, 10u, 5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 
-	3u, 17u, 4u, 13u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 
-	1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u, 
-	5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 1u, 17u, 
-	3u, 17u, 1u, 17u, 4u, 13u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 3u, 10u, 
-	5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 0
-};
-
-static const char _indic_syllable_machine_key_spans[] = {
-	1, 5, 3, 4, 5, 1, 1, 5, 
-	10, 5, 1, 3, 4, 5, 1, 1, 
-	5, 10, 10, 10, 1, 3, 4, 5, 
-	1, 1, 5, 5, 10, 1, 3, 4, 
-	5, 1, 1, 5, 5, 4, 1, 19, 
-	15, 15, 14, 16, 6, 6, 1, 6, 
-	16, 16, 16, 8, 7, 6, 7, 6, 
-	8, 6, 15, 15, 15, 15, 14, 16, 
-	15, 15, 14, 16, 6, 1, 6, 16, 
-	16, 8, 7, 6, 7, 6, 6, 8, 
-	6, 15, 15, 5, 15, 15, 14, 16, 
-	15, 16, 6, 1, 6, 16, 16, 8, 
-	7, 6, 15, 7, 6, 6, 8, 6, 
-	15, 10, 5, 15, 15, 14, 16, 15, 
-	16, 6, 1, 6, 16, 16, 8, 7, 
-	6, 15, 7, 6, 6, 8, 6, 17, 
-	15, 17, 10, 6, 1, 6, 16, 8, 
-	6, 6, 1, 6, 16
-};
-
-static const short _indic_syllable_machine_index_offsets[] = {
-	0, 2, 8, 12, 17, 23, 25, 27, 
-	33, 44, 50, 52, 56, 61, 67, 69, 
-	71, 77, 88, 99, 110, 112, 116, 121, 
-	127, 129, 131, 137, 143, 154, 156, 160, 
-	165, 171, 173, 175, 181, 187, 192, 194, 
-	214, 230, 246, 261, 278, 285, 292, 294, 
-	301, 318, 335, 352, 361, 369, 376, 384, 
-	391, 400, 407, 423, 439, 455, 471, 486, 
-	503, 519, 535, 550, 567, 574, 576, 583, 
-	600, 617, 626, 634, 641, 649, 656, 663, 
-	672, 679, 695, 711, 717, 733, 749, 764, 
-	781, 797, 814, 821, 823, 830, 847, 864, 
-	873, 881, 888, 904, 912, 919, 926, 935, 
-	942, 958, 969, 975, 991, 1007, 1022, 1039, 
-	1055, 1072, 1079, 1081, 1088, 1105, 1122, 1131, 
-	1139, 1146, 1162, 1170, 1177, 1184, 1193, 1200, 
-	1218, 1234, 1252, 1263, 1270, 1272, 1279, 1296, 
-	1305, 1312, 1319, 1321, 1328
-};
-
-static const unsigned char _indic_syllable_machine_indicies[] = {
-	1, 0, 2, 3, 3, 4, 1, 0, 
-	3, 3, 4, 0, 3, 3, 4, 1, 
-	0, 5, 3, 3, 4, 1, 0, 6, 
-	0, 7, 0, 8, 3, 3, 4, 1, 
-	0, 2, 3, 3, 4, 1, 0, 0, 
-	0, 0, 9, 0, 11, 12, 12, 13, 
-	14, 10, 14, 10, 12, 12, 13, 10, 
-	12, 12, 13, 14, 10, 15, 12, 12, 
-	13, 14, 10, 16, 10, 17, 10, 18, 
-	12, 12, 13, 14, 10, 11, 12, 12, 
-	13, 14, 10, 10, 10, 10, 19, 10, 
-	11, 12, 12, 13, 14, 10, 10, 10, 
-	10, 20, 10, 22, 23, 23, 24, 25, 
-	21, 21, 21, 21, 26, 21, 25, 21, 
-	23, 23, 24, 27, 23, 23, 24, 25, 
-	21, 28, 23, 23, 24, 25, 21, 29, 
-	21, 30, 21, 22, 23, 23, 24, 25, 
-	21, 31, 23, 23, 24, 25, 21, 33, 
-	34, 34, 35, 36, 32, 32, 32, 32, 
-	37, 32, 36, 32, 34, 34, 35, 32, 
-	34, 34, 35, 36, 32, 38, 34, 34, 
-	35, 36, 32, 39, 32, 40, 32, 33, 
-	34, 34, 35, 36, 32, 41, 34, 34, 
-	35, 36, 32, 23, 23, 24, 1, 0, 
-	43, 42, 45, 46, 47, 48, 49, 50, 
-	24, 25, 44, 51, 52, 52, 26, 44, 
-	53, 54, 55, 56, 57, 44, 59, 60, 
-	61, 62, 4, 1, 58, 63, 58, 58, 
-	9, 58, 58, 58, 64, 58, 65, 60, 
-	66, 66, 4, 1, 58, 63, 58, 58, 
-	58, 58, 58, 58, 64, 58, 60, 66, 
-	66, 4, 1, 58, 63, 58, 58, 58, 
-	58, 58, 58, 64, 58, 45, 58, 58, 
-	58, 67, 68, 58, 1, 58, 63, 58, 
-	58, 58, 58, 58, 45, 58, 69, 69, 
-	58, 1, 58, 63, 58, 63, 58, 58, 
-	70, 58, 63, 58, 63, 58, 63, 58, 
-	58, 58, 58, 63, 58, 45, 58, 71, 
-	58, 69, 69, 58, 1, 58, 63, 58, 
-	58, 58, 58, 58, 45, 58, 45, 58, 
-	58, 58, 69, 69, 58, 1, 58, 63, 
-	58, 58, 58, 58, 58, 45, 58, 45, 
-	58, 58, 58, 69, 68, 58, 1, 58, 
-	63, 58, 58, 58, 58, 58, 45, 58, 
-	72, 7, 73, 74, 4, 1, 58, 63, 
-	58, 7, 73, 74, 4, 1, 58, 63, 
-	58, 73, 73, 4, 1, 58, 63, 58, 
-	75, 76, 76, 4, 1, 58, 63, 58, 
-	67, 77, 58, 1, 58, 63, 58, 67, 
-	58, 69, 69, 58, 1, 58, 63, 58, 
-	69, 77, 58, 1, 58, 63, 58, 59, 
-	60, 66, 66, 4, 1, 58, 63, 58, 
-	58, 58, 58, 58, 58, 64, 58, 59, 
-	60, 61, 66, 4, 1, 58, 63, 58, 
-	58, 9, 58, 58, 58, 64, 58, 79, 
-	80, 81, 82, 13, 14, 78, 83, 78, 
-	78, 20, 78, 78, 78, 84, 78, 85, 
-	80, 86, 82, 13, 14, 78, 83, 78, 
-	78, 78, 78, 78, 78, 84, 78, 80, 
-	86, 82, 13, 14, 78, 83, 78, 78, 
-	78, 78, 78, 78, 84, 78, 87, 78, 
-	78, 78, 88, 89, 78, 14, 78, 83, 
-	78, 78, 78, 78, 78, 87, 78, 90, 
-	80, 91, 92, 13, 14, 78, 83, 78, 
-	78, 19, 78, 78, 78, 84, 78, 93, 
-	80, 86, 86, 13, 14, 78, 83, 78, 
-	78, 78, 78, 78, 78, 84, 78, 80, 
-	86, 86, 13, 14, 78, 83, 78, 78, 
-	78, 78, 78, 78, 84, 78, 87, 78, 
-	78, 78, 94, 89, 78, 14, 78, 83, 
-	78, 78, 78, 78, 78, 87, 78, 83, 
-	78, 78, 95, 78, 83, 78, 83, 78, 
-	83, 78, 78, 78, 78, 83, 78, 87, 
-	78, 96, 78, 94, 94, 78, 14, 78, 
-	83, 78, 78, 78, 78, 78, 87, 78, 
-	87, 78, 78, 78, 94, 94, 78, 14, 
-	78, 83, 78, 78, 78, 78, 78, 87, 
-	78, 97, 17, 98, 99, 13, 14, 78, 
-	83, 78, 17, 98, 99, 13, 14, 78, 
-	83, 78, 98, 98, 13, 14, 78, 83, 
-	78, 100, 101, 101, 13, 14, 78, 83, 
-	78, 88, 102, 78, 14, 78, 83, 78, 
-	94, 94, 78, 14, 78, 83, 78, 88, 
-	78, 94, 94, 78, 14, 78, 83, 78, 
-	94, 102, 78, 14, 78, 83, 78, 90, 
-	80, 86, 86, 13, 14, 78, 83, 78, 
-	78, 78, 78, 78, 78, 84, 78, 90, 
-	80, 91, 86, 13, 14, 78, 83, 78, 
-	78, 19, 78, 78, 78, 84, 78, 11, 
-	12, 12, 13, 14, 78, 79, 80, 86, 
-	82, 13, 14, 78, 83, 78, 78, 78, 
-	78, 78, 78, 84, 78, 104, 48, 105, 
-	105, 24, 25, 103, 51, 103, 103, 103, 
-	103, 103, 103, 55, 103, 48, 105, 105, 
-	24, 25, 103, 51, 103, 103, 103, 103, 
-	103, 103, 55, 103, 106, 103, 103, 103, 
-	107, 108, 103, 25, 103, 51, 103, 103, 
-	103, 103, 103, 106, 103, 47, 48, 109, 
-	110, 24, 25, 103, 51, 103, 103, 26, 
-	103, 103, 103, 55, 103, 106, 103, 103, 
-	103, 111, 108, 103, 25, 103, 51, 103, 
-	103, 103, 103, 103, 106, 103, 51, 103, 
-	103, 112, 103, 51, 103, 51, 103, 51, 
-	103, 103, 103, 103, 51, 103, 106, 103, 
-	113, 103, 111, 111, 103, 25, 103, 51, 
-	103, 103, 103, 103, 103, 106, 103, 106, 
-	103, 103, 103, 111, 111, 103, 25, 103, 
-	51, 103, 103, 103, 103, 103, 106, 103, 
-	114, 30, 115, 116, 24, 25, 103, 51, 
-	103, 30, 115, 116, 24, 25, 103, 51, 
-	103, 115, 115, 24, 25, 103, 51, 103, 
-	47, 48, 105, 105, 24, 25, 103, 51, 
-	103, 103, 103, 103, 103, 103, 55, 103, 
-	117, 118, 118, 24, 25, 103, 51, 103, 
-	107, 119, 103, 25, 103, 51, 103, 111, 
-	111, 103, 25, 103, 51, 103, 107, 103, 
-	111, 111, 103, 25, 103, 51, 103, 111, 
-	119, 103, 25, 103, 51, 103, 47, 48, 
-	109, 105, 24, 25, 103, 51, 103, 103, 
-	26, 103, 103, 103, 55, 103, 22, 23, 
-	23, 24, 25, 120, 120, 120, 120, 26, 
-	120, 22, 23, 23, 24, 25, 120, 122, 
-	123, 124, 125, 35, 36, 121, 126, 121, 
-	121, 37, 121, 121, 121, 127, 121, 128, 
-	123, 125, 125, 35, 36, 121, 126, 121, 
-	121, 121, 121, 121, 121, 127, 121, 123, 
-	125, 125, 35, 36, 121, 126, 121, 121, 
-	121, 121, 121, 121, 127, 121, 129, 121, 
-	121, 121, 130, 131, 121, 36, 121, 126, 
-	121, 121, 121, 121, 121, 129, 121, 122, 
-	123, 124, 52, 35, 36, 121, 126, 121, 
-	121, 37, 121, 121, 121, 127, 121, 129, 
-	121, 121, 121, 132, 131, 121, 36, 121, 
-	126, 121, 121, 121, 121, 121, 129, 121, 
-	126, 121, 121, 133, 121, 126, 121, 126, 
-	121, 126, 121, 121, 121, 121, 126, 121, 
-	129, 121, 134, 121, 132, 132, 121, 36, 
-	121, 126, 121, 121, 121, 121, 121, 129, 
-	121, 129, 121, 121, 121, 132, 132, 121, 
-	36, 121, 126, 121, 121, 121, 121, 121, 
-	129, 121, 135, 40, 136, 137, 35, 36, 
-	121, 126, 121, 40, 136, 137, 35, 36, 
-	121, 126, 121, 136, 136, 35, 36, 121, 
-	126, 121, 122, 123, 125, 125, 35, 36, 
-	121, 126, 121, 121, 121, 121, 121, 121, 
-	127, 121, 138, 139, 139, 35, 36, 121, 
-	126, 121, 130, 140, 121, 36, 121, 126, 
-	121, 132, 132, 121, 36, 121, 126, 121, 
-	130, 121, 132, 132, 121, 36, 121, 126, 
-	121, 132, 140, 121, 36, 121, 126, 121, 
-	45, 46, 47, 48, 109, 105, 24, 25, 
-	103, 51, 52, 52, 26, 103, 103, 45, 
-	55, 103, 59, 141, 61, 62, 4, 1, 
-	58, 63, 58, 58, 9, 58, 58, 58, 
-	64, 58, 45, 46, 47, 48, 142, 143, 
-	24, 144, 58, 145, 58, 52, 26, 58, 
-	58, 45, 55, 58, 22, 146, 146, 24, 
-	144, 58, 63, 58, 58, 26, 58, 145, 
-	58, 58, 147, 58, 145, 58, 145, 58, 
-	145, 58, 58, 58, 58, 145, 58, 45, 
-	58, 71, 22, 146, 146, 24, 144, 58, 
-	63, 58, 58, 58, 58, 58, 45, 58, 
-	149, 148, 150, 150, 148, 43, 148, 151, 
-	148, 150, 150, 148, 43, 148, 151, 148, 
-	151, 148, 148, 152, 148, 151, 148, 151, 
-	148, 151, 148, 148, 148, 148, 151, 148, 
-	45, 120, 120, 120, 120, 120, 120, 120, 
-	120, 120, 52, 120, 120, 120, 120, 45, 
-	120, 0
-};
-
-static const unsigned char _indic_syllable_machine_trans_targs[] = {
-	39, 45, 50, 2, 51, 5, 6, 53, 
-	57, 58, 39, 67, 11, 73, 68, 14, 
-	15, 75, 80, 81, 84, 39, 89, 21, 
-	95, 90, 98, 39, 24, 25, 97, 103, 
-	39, 112, 30, 118, 113, 121, 33, 34, 
-	120, 126, 39, 137, 39, 40, 60, 85, 
-	87, 105, 106, 91, 107, 127, 128, 99, 
-	135, 140, 39, 41, 43, 8, 59, 46, 
-	54, 42, 1, 44, 48, 0, 47, 49, 
-	52, 3, 4, 55, 7, 56, 39, 61, 
-	63, 18, 83, 69, 76, 62, 9, 64, 
-	78, 71, 65, 17, 82, 66, 10, 70, 
-	72, 74, 12, 13, 77, 16, 79, 39, 
-	86, 26, 88, 101, 93, 19, 104, 20, 
-	92, 94, 96, 22, 23, 100, 27, 102, 
-	39, 39, 108, 110, 28, 35, 114, 122, 
-	109, 111, 124, 116, 29, 115, 117, 119, 
-	31, 32, 123, 36, 125, 129, 130, 134, 
-	131, 132, 37, 133, 39, 136, 38, 138, 
-	139
-};
-
-static const char _indic_syllable_machine_trans_actions[] = {
-	1, 0, 2, 0, 2, 0, 0, 2, 
-	2, 2, 3, 2, 0, 2, 0, 0, 
-	0, 2, 2, 2, 2, 4, 2, 0, 
-	5, 0, 5, 6, 0, 0, 5, 2, 
-	7, 2, 0, 2, 0, 2, 0, 0, 
-	2, 2, 8, 0, 11, 2, 2, 5, 
-	0, 12, 12, 0, 2, 5, 2, 5, 
-	2, 0, 13, 2, 0, 0, 2, 0, 
-	2, 2, 0, 2, 2, 0, 0, 2, 
-	2, 0, 0, 0, 0, 2, 14, 2, 
-	0, 0, 2, 0, 2, 2, 0, 2, 
-	2, 2, 2, 0, 2, 2, 0, 0, 
-	2, 2, 0, 0, 0, 0, 2, 15, 
-	5, 0, 5, 2, 2, 0, 5, 0, 
-	0, 2, 5, 0, 0, 0, 0, 2, 
-	16, 17, 2, 0, 0, 0, 0, 2, 
-	2, 2, 2, 2, 0, 0, 2, 2, 
-	0, 0, 0, 0, 2, 0, 18, 18, 
-	0, 0, 0, 0, 19, 2, 0, 0, 
-	0
-};
-
-static const char _indic_syllable_machine_to_state_actions[] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 9, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0
-};
-
-static const char _indic_syllable_machine_from_state_actions[] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 10, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0
-};
-
-static const short _indic_syllable_machine_eof_trans[] = {
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 11, 11, 11, 11, 11, 11, 11, 
-	11, 11, 11, 22, 22, 28, 22, 22, 
-	22, 22, 22, 22, 33, 33, 33, 33, 
-	33, 33, 33, 33, 33, 1, 43, 0, 
-	59, 59, 59, 59, 59, 59, 59, 59, 
-	59, 59, 59, 59, 59, 59, 59, 59, 
-	59, 59, 59, 59, 79, 79, 79, 79, 
-	79, 79, 79, 79, 79, 79, 79, 79, 
-	79, 79, 79, 79, 79, 79, 79, 79, 
-	79, 79, 79, 79, 79, 104, 104, 104, 
-	104, 104, 104, 104, 104, 104, 104, 104, 
-	104, 104, 104, 104, 104, 104, 104, 104, 
-	104, 121, 121, 122, 122, 122, 122, 122, 
-	122, 122, 122, 122, 122, 122, 122, 122, 
-	122, 122, 122, 122, 122, 122, 122, 104, 
-	59, 59, 59, 59, 59, 59, 59, 149, 
-	149, 149, 149, 149, 121
-};
-
-static const int indic_syllable_machine_start = 39;
-static const int indic_syllable_machine_first_final = 39;
-static const int indic_syllable_machine_error = -1;
-
-static const int indic_syllable_machine_en_main = 39;
-
-
-#line 46 "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) | syllable_type; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
-
-static void
-find_syllables_indic (hb_buffer_t *buffer)
-{
-  unsigned int p, pe, eof, ts, te, act;
-  int cs;
-  hb_glyph_info_t *info = buffer->info;
-  
-#line 440 "hb-ot-shape-complex-indic-machine.hh"
-	{
-	cs = indic_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
-	}
-
-#line 122 "hb-ot-shape-complex-indic-machine.rl"
-
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int syllable_serial = 1;
-  
-#line 456 "hb-ot-shape-complex-indic-machine.hh"
-	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const unsigned char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-_resume:
-	switch ( _indic_syllable_machine_from_state_actions[cs] ) {
-	case 10:
-#line 1 "NONE"
-	{ts = p;}
-	break;
-#line 470 "hb-ot-shape-complex-indic-machine.hh"
-	}
-
-	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
-	_inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs];
-
-	_slen = _indic_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) &&
-		( info[p].indic_category()) <= _keys[1] ?
-		( info[p].indic_category()) - _keys[0] : _slen ];
-
-_eof_trans:
-	cs = _indic_syllable_machine_trans_targs[_trans];
-
-	if ( _indic_syllable_machine_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _indic_syllable_machine_trans_actions[_trans] ) {
-	case 2:
-#line 1 "NONE"
-	{te = p+1;}
-	break;
-	case 11:
-#line 98 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p+1;{ found_syllable (indic_non_indic_cluster); }}
-	break;
-	case 13:
-#line 93 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (indic_consonant_syllable); }}
-	break;
-	case 14:
-#line 94 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (indic_vowel_syllable); }}
-	break;
-	case 17:
-#line 95 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (indic_standalone_cluster); }}
-	break;
-	case 19:
-#line 96 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (indic_symbol_cluster); }}
-	break;
-	case 15:
-#line 97 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (indic_broken_cluster); }}
-	break;
-	case 16:
-#line 98 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (indic_non_indic_cluster); }}
-	break;
-	case 1:
-#line 93 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (indic_consonant_syllable); }}
-	break;
-	case 3:
-#line 94 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (indic_vowel_syllable); }}
-	break;
-	case 7:
-#line 95 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (indic_standalone_cluster); }}
-	break;
-	case 8:
-#line 96 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (indic_symbol_cluster); }}
-	break;
-	case 4:
-#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 (indic_consonant_syllable); }
-	break;
-	case 5:
-	{{p = ((te))-1;} found_syllable (indic_broken_cluster); }
-	break;
-	case 6:
-	{{p = ((te))-1;} found_syllable (indic_non_indic_cluster); }
-	break;
-	}
-	}
-	break;
-	case 18:
-#line 1 "NONE"
-	{te = p+1;}
-#line 93 "hb-ot-shape-complex-indic-machine.rl"
-	{act = 1;}
-	break;
-	case 5:
-#line 1 "NONE"
-	{te = p+1;}
-#line 97 "hb-ot-shape-complex-indic-machine.rl"
-	{act = 5;}
-	break;
-	case 12:
-#line 1 "NONE"
-	{te = p+1;}
-#line 98 "hb-ot-shape-complex-indic-machine.rl"
-	{act = 6;}
-	break;
-#line 573 "hb-ot-shape-complex-indic-machine.hh"
-	}
-
-_again:
-	switch ( _indic_syllable_machine_to_state_actions[cs] ) {
-	case 9:
-#line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 582 "hb-ot-shape-complex-indic-machine.hh"
-	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	if ( _indic_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _indic_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
-	}
-	}
-
-	}
-
-#line 130 "hb-ot-shape-complex-indic-machine.rl"
-
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-table.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-table.cc
deleted file mode 100644
index 326aa9f96e12bcc1bf26a3b295b4e3f9d33c4cfb..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-table.cc
+++ /dev/null
@@ -1,501 +0,0 @@
-/* == Start of generated table == */
-/*
- * The following table is generated by running:
- *
- *   ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt
- *
- * on files with these headers:
- *
- * # IndicSyllabicCategory-14.0.0.txt
- * # Date: 2021-05-22, 01:01:00 GMT [KW, RP]
- * # IndicPositionalCategory-14.0.0.txt
- * # Date: 2021-05-22, 01:01:00 GMT [KW, RP]
- * # Blocks-14.0.0.txt
- * # Date: 2021-01-22, 23:29:00 GMT [KW]
- */
-
-#include "hb.hh"
-
-#ifndef HB_NO_OT_SHAPE
-
-#include "hb-ot-shape-complex-indic.hh"
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-macros"
-
-#define ISC_A    INDIC_SYLLABIC_CATEGORY_AVAGRAHA                    /*   17 chars; Avagraha */
-#define ISC_Bi   INDIC_SYLLABIC_CATEGORY_BINDU                       /*   91 chars; Bindu */
-#define ISC_BJN  INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER       /*   20 chars; Brahmi_Joining_Number */
-#define ISC_Ca   INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK           /*   59 chars; Cantillation_Mark */
-#define ISC_C    INDIC_SYLLABIC_CATEGORY_CONSONANT                   /* 2206 chars; Consonant */
-#define ISC_CD   INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD              /*   14 chars; Consonant_Dead */
-#define ISC_CF   INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL             /*   70 chars; Consonant_Final */
-#define ISC_CHL  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER       /*    5 chars; Consonant_Head_Letter */
-#define ISC_CIP  INDIC_SYLLABIC_CATEGORY_CONSONANT_INITIAL_POSTFIXED /*    1 chars; Consonant_Initial_Postfixed */
-#define ISC_CK   INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER            /*    2 chars; Consonant_Killer */
-#define ISC_CM   INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL            /*   31 chars; Consonant_Medial */
-#define ISC_CP   INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER       /*   22 chars; Consonant_Placeholder */
-#define ISC_CPR  INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA   /*    3 chars; Consonant_Preceding_Repha */
-#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED          /*   10 chars; Consonant_Prefixed */
-#define ISC_CS   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED         /*   94 chars; Consonant_Subjoined */
-#define ISC_CSR  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA  /*    1 chars; Consonant_Succeeding_Repha */
-#define ISC_CWS  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER      /*    8 chars; Consonant_With_Stacker */
-#define ISC_GM   INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK             /*    3 chars; Gemination_Mark */
-#define ISC_IS   INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER           /*   12 chars; Invisible_Stacker */
-#define ISC_ZWJ  INDIC_SYLLABIC_CATEGORY_JOINER                      /*    1 chars; Joiner */
-#define ISC_ML   INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER            /*    1 chars; Modifying_Letter */
-#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER                  /*    1 chars; Non_Joiner */
-#define ISC_N    INDIC_SYLLABIC_CATEGORY_NUKTA                       /*   32 chars; Nukta */
-#define ISC_Nd   INDIC_SYLLABIC_CATEGORY_NUMBER                      /*  491 chars; Number */
-#define ISC_NJ   INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER               /*    1 chars; Number_Joiner */
-#define ISC_x    INDIC_SYLLABIC_CATEGORY_OTHER                       /*    1 chars; Other */
-#define ISC_PK   INDIC_SYLLABIC_CATEGORY_PURE_KILLER                 /*   25 chars; Pure_Killer */
-#define ISC_RS   INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER            /*    2 chars; Register_Shifter */
-#define ISC_SM   INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER           /*   25 chars; Syllable_Modifier */
-#define ISC_TL   INDIC_SYLLABIC_CATEGORY_TONE_LETTER                 /*    7 chars; Tone_Letter */
-#define ISC_TM   INDIC_SYLLABIC_CATEGORY_TONE_MARK                   /*   42 chars; Tone_Mark */
-#define ISC_V    INDIC_SYLLABIC_CATEGORY_VIRAMA                      /*   27 chars; Virama */
-#define ISC_Vs   INDIC_SYLLABIC_CATEGORY_VISARGA                     /*   35 chars; Visarga */
-#define ISC_Vo   INDIC_SYLLABIC_CATEGORY_VOWEL                       /*   30 chars; Vowel */
-#define ISC_M    INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT             /*  686 chars; Vowel_Dependent */
-#define ISC_VI   INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT           /*  486 chars; Vowel_Independent */
-
-#define IMC_B    INDIC_MATRA_CATEGORY_BOTTOM                         /*  352 chars; Bottom */
-#define IMC_BL   INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT                /*    1 chars; Bottom_And_Left */
-#define IMC_BR   INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT               /*    4 chars; Bottom_And_Right */
-#define IMC_L    INDIC_MATRA_CATEGORY_LEFT                           /*   64 chars; Left */
-#define IMC_LR   INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT                 /*   22 chars; Left_And_Right */
-#define IMC_x    INDIC_MATRA_CATEGORY_NOT_APPLICABLE                 /*    1 chars; Not_Applicable */
-#define IMC_O    INDIC_MATRA_CATEGORY_OVERSTRUCK                     /*   10 chars; Overstruck */
-#define IMC_R    INDIC_MATRA_CATEGORY_RIGHT                          /*  290 chars; Right */
-#define IMC_T    INDIC_MATRA_CATEGORY_TOP                            /*  418 chars; Top */
-#define IMC_TB   INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM                 /*   10 chars; Top_And_Bottom */
-#define IMC_TBL  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT        /*    2 chars; Top_And_Bottom_And_Left */
-#define IMC_TBR  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT       /*    1 chars; Top_And_Bottom_And_Right */
-#define IMC_TL   INDIC_MATRA_CATEGORY_TOP_AND_LEFT                   /*    6 chars; Top_And_Left */
-#define IMC_TLR  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT         /*    4 chars; Top_And_Left_And_Right */
-#define IMC_TR   INDIC_MATRA_CATEGORY_TOP_AND_RIGHT                  /*   13 chars; Top_And_Right */
-#define IMC_VOL  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT              /*   19 chars; Visual_Order_Left */
-
-#pragma GCC diagnostic pop
-
-#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)
-
-
-static const uint16_t indic_table[] = {
-
-
-#define indic_offset_0x0028u 0
-
-
-  /* Basic Latin */
-
-  /* 0028 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(CP,x),  _(x,x),  _(x,x),
-  /* 0030 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0038 */ _(Nd,x), _(Nd,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-#define indic_offset_0x00b0u 24
-
-
-  /* Latin-1 Supplement */
-
-  /* 00B0 */  _(x,x),  _(x,x), _(SM,x), _(SM,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 00B8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 00C0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 00C8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 00D0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(CP,x),
-
-#define indic_offset_0x0900u 64
-
-
-  /* Devanagari */
-
-  /* 0900 */ _(Bi,T), _(Bi,T), _(Bi,T), _(Vs,R), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0908 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0910 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0918 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0920 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0928 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0930 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0938 */  _(C,x),  _(C,x),  _(M,T),  _(M,R),  _(N,B),  _(A,x),  _(M,R),  _(M,L),
-  /* 0940 */  _(M,R),  _(M,B),  _(M,B),  _(M,B),  _(M,B),  _(M,T),  _(M,T),  _(M,T),
-  /* 0948 */  _(M,T),  _(M,R),  _(M,R),  _(M,R),  _(M,R),  _(V,B),  _(M,L),  _(M,R),
-  /* 0950 */  _(x,x), _(Ca,T), _(Ca,B),  _(x,T),  _(x,T),  _(M,T),  _(M,B),  _(M,B),
-  /* 0958 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0960 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0968 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0970 */  _(x,x),  _(x,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0978 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-
-  /* Bengali */
-
-  /* 0980 */ _(CP,x), _(Bi,T), _(Bi,R), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0988 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x),  _(x,x), _(VI,x),
-  /* 0990 */ _(VI,x),  _(x,x),  _(x,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0998 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 09A0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 09A8 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 09B0 */  _(C,x),  _(x,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),  _(C,x),  _(C,x),
-  /* 09B8 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(N,B),  _(A,x),  _(M,R),  _(M,L),
-  /* 09C0 */  _(M,R),  _(M,B),  _(M,B),  _(M,B),  _(M,B),  _(x,x),  _(x,x),  _(M,L),
-  /* 09C8 */  _(M,L),  _(x,x),  _(x,x), _(M,LR), _(M,LR),  _(V,B), _(CD,x),  _(x,x),
-  /* 09D0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,R),
-  /* 09D8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),
-  /* 09E0 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 09E8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 09F0 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 09F8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(Bi,x),  _(x,x), _(SM,T),  _(x,x),
-
-  /* Gurmukhi */
-
-  /* 0A00 */  _(x,x), _(Bi,T), _(Bi,T), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0A08 */ _(VI,x), _(VI,x), _(VI,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(VI,x),
-  /* 0A10 */ _(VI,x),  _(x,x),  _(x,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0A18 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0A20 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0A28 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0A30 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(x,x),
-  /* 0A38 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(N,B),  _(x,x),  _(M,R),  _(M,L),
-  /* 0A40 */  _(M,R),  _(M,B),  _(M,B),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,T),
-  /* 0A48 */  _(M,T),  _(x,x),  _(x,x),  _(M,T),  _(M,T),  _(V,B),  _(x,x),  _(x,x),
-  /* 0A50 */  _(x,x), _(Ca,B),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0A58 */  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(x,x),
-  /* 0A60 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0A68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0A70 */ _(Bi,T), _(GM,T), _(CP,x), _(CP,x),  _(x,x), _(CM,B),  _(x,x),  _(x,x),
-  /* 0A78 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-  /* Gujarati */
-
-  /* 0A80 */  _(x,x), _(Bi,T), _(Bi,T), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0A88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x), _(VI,x),
-  /* 0A90 */ _(VI,x), _(VI,x),  _(x,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0A98 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0AA0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0AA8 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0AB0 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0AB8 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(N,B),  _(A,x),  _(M,R),  _(M,L),
-  /* 0AC0 */  _(M,R),  _(M,B),  _(M,B),  _(M,B),  _(M,B),  _(M,T),  _(x,x),  _(M,T),
-  /* 0AC8 */  _(M,T), _(M,TR),  _(x,x),  _(M,R),  _(M,R),  _(V,B),  _(x,x),  _(x,x),
-  /* 0AD0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0AD8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0AE0 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0AE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0AF0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0AF8 */  _(x,x),  _(C,x), _(Ca,T), _(Ca,T), _(Ca,T),  _(N,T),  _(N,T),  _(N,T),
-
-  /* Oriya */
-
-  /* 0B00 */  _(x,x), _(Bi,T), _(Bi,R), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0B08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x),  _(x,x), _(VI,x),
-  /* 0B10 */ _(VI,x),  _(x,x),  _(x,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0B18 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0B20 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0B28 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0B30 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0B38 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(N,B),  _(A,x),  _(M,R),  _(M,T),
-  /* 0B40 */  _(M,R),  _(M,B),  _(M,B),  _(M,B),  _(M,B),  _(x,x),  _(x,x),  _(M,L),
-  /* 0B48 */ _(M,TL),  _(x,x),  _(x,x), _(M,LR),_(M,TLR),  _(V,B),  _(x,x),  _(x,x),
-  /* 0B50 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,T),  _(M,T), _(M,TR),
-  /* 0B58 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),
-  /* 0B60 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0B68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0B70 */  _(x,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0B78 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-  /* Tamil */
-
-  /* 0B80 */  _(x,x),  _(x,x), _(Bi,T), _(ML,x),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0B88 */ _(VI,x), _(VI,x), _(VI,x),  _(x,x),  _(x,x),  _(x,x), _(VI,x), _(VI,x),
-  /* 0B90 */ _(VI,x),  _(x,x), _(VI,x), _(VI,x), _(VI,x),  _(C,x),  _(x,x),  _(x,x),
-  /* 0B98 */  _(x,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(x,x),  _(C,x),  _(C,x),
-  /* 0BA0 */  _(x,x),  _(x,x),  _(x,x),  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0BA8 */  _(C,x),  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),  _(C,x),  _(C,x),
-  /* 0BB0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0BB8 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,R),  _(M,R),
-  /* 0BC0 */  _(M,T),  _(M,R),  _(M,R),  _(x,x),  _(x,x),  _(x,x),  _(M,L),  _(M,L),
-  /* 0BC8 */  _(M,L),  _(x,x), _(M,LR), _(M,LR), _(M,LR),  _(V,T),  _(x,x),  _(x,x),
-  /* 0BD0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,R),
-  /* 0BD8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0BE0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0BE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0BF0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0BF8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-  /* Telugu */
-
-  /* 0C00 */ _(Bi,T), _(Bi,R), _(Bi,R), _(Vs,R), _(Bi,T), _(VI,x), _(VI,x), _(VI,x),
-  /* 0C08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x), _(VI,x), _(VI,x),
-  /* 0C10 */ _(VI,x),  _(x,x), _(VI,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0C18 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0C20 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0C28 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0C30 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0C38 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(N,B),  _(A,x),  _(M,T),  _(M,T),
-  /* 0C40 */  _(M,T),  _(M,R),  _(M,R),  _(M,R),  _(M,R),  _(x,x),  _(M,T),  _(M,T),
-  /* 0C48 */ _(M,TB),  _(x,x),  _(M,T),  _(M,T),  _(M,T),  _(V,T),  _(x,x),  _(x,x),
-  /* 0C50 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,T),  _(M,B),  _(x,x),
-  /* 0C58 */  _(C,x),  _(C,x),  _(C,x),  _(x,x),  _(x,x), _(CD,x),  _(x,x),  _(x,x),
-  /* 0C60 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0C68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0C70 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0C78 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-  /* Kannada */
-
-  /* 0C80 */ _(Bi,x), _(Bi,T), _(Bi,R), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0C88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x), _(VI,x), _(VI,x),
-  /* 0C90 */ _(VI,x),  _(x,x), _(VI,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0C98 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0CA0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0CA8 */  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0CB0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0CB8 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(N,B),  _(A,x),  _(M,R),  _(M,T),
-  /* 0CC0 */ _(M,TR),  _(M,R),  _(M,R),  _(M,R),  _(M,R),  _(x,x),  _(M,T), _(M,TR),
-  /* 0CC8 */ _(M,TR),  _(x,x), _(M,TR), _(M,TR),  _(M,T),  _(V,T),  _(x,x),  _(x,x),
-  /* 0CD0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,R),  _(M,R),  _(x,x),
-  /* 0CD8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(CD,x),  _(C,x),  _(x,x),
-  /* 0CE0 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0CE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0CF0 */  _(x,x),_(CWS,x),_(CWS,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0CF8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-  /* Malayalam */
-
-  /* 0D00 */ _(Bi,T), _(Bi,T), _(Bi,R), _(Vs,R), _(Bi,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0D08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x), _(VI,x), _(VI,x),
-  /* 0D10 */ _(VI,x),  _(x,x), _(VI,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0D18 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0D20 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0D28 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0D30 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0D38 */  _(C,x),  _(C,x),  _(C,x), _(PK,T), _(PK,T),  _(A,x),  _(M,R),  _(M,R),
-  /* 0D40 */  _(M,R),  _(M,R),  _(M,R),  _(M,B),  _(M,B),  _(x,x),  _(M,L),  _(M,L),
-  /* 0D48 */  _(M,L),  _(x,x), _(M,LR), _(M,LR), _(M,LR),  _(V,T),_(CPR,T),  _(x,x),
-  /* 0D50 */  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(CD,x), _(CD,x), _(CD,x),  _(M,R),
-  /* 0D58 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(VI,x),
-  /* 0D60 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0D68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0D70 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0D78 */  _(x,x),  _(x,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x),
-
-  /* Sinhala */
-
-  /* 0D80 */  _(x,x), _(Bi,T), _(Bi,R), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0D88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 0D90 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x),
-  /* 0D98 */  _(x,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0DA0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0DA8 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0DB0 */  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0DB8 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(x,x),  _(C,x),  _(x,x),  _(x,x),
-  /* 0DC0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(x,x),
-  /* 0DC8 */  _(x,x),  _(x,x),  _(V,T),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(M,R),
-  /* 0DD0 */  _(M,R),  _(M,R),  _(M,T),  _(M,T),  _(M,B),  _(x,x),  _(M,B),  _(x,x),
-  /* 0DD8 */  _(M,R),  _(M,L), _(M,TL),  _(M,L), _(M,LR),_(M,TLR), _(M,LR),  _(M,R),
-  /* 0DE0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
-  /* 0DE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 0DF0 */  _(x,x),  _(x,x),  _(M,R),  _(M,R),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-#define indic_offset_0x1000u 1336
-
-
-  /* Myanmar */
-
-  /* 1000 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1008 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1010 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1018 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1020 */  _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 1028 */ _(VI,x), _(VI,x), _(VI,x),  _(M,R),  _(M,R),  _(M,T),  _(M,T),  _(M,B),
-  /* 1030 */  _(M,B),  _(M,L),  _(M,T),  _(M,T),  _(M,T),  _(M,T), _(Bi,T), _(TM,B),
-  /* 1038 */ _(Vs,R), _(IS,x), _(PK,T), _(CM,R),_(CM,TBL), _(CM,B), _(CM,B),  _(C,x),
-  /* 1040 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 1048 */ _(Nd,x), _(Nd,x),  _(x,x), _(CP,x),  _(x,x),  _(x,x), _(CP,x),  _(x,x),
-  /* 1050 */  _(C,x),  _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(M,R),  _(M,R),
-  /* 1058 */  _(M,B),  _(M,B),  _(C,x),  _(C,x),  _(C,x),  _(C,x), _(CM,B), _(CM,B),
-  /* 1060 */ _(CM,B),  _(C,x),  _(M,R), _(TM,R), _(TM,R),  _(C,x),  _(C,x),  _(M,R),
-  /* 1068 */  _(M,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R),  _(C,x),  _(C,x),
-  /* 1070 */  _(C,x),  _(M,T),  _(M,T),  _(M,T),  _(M,T),  _(C,x),  _(C,x),  _(C,x),
-  /* 1078 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1080 */  _(C,x),  _(C,x), _(CM,B),  _(M,R),  _(M,L),  _(M,T),  _(M,T), _(TM,R),
-  /* 1088 */ _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,B),  _(C,x), _(TM,R),
-  /* 1090 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 1098 */ _(Nd,x), _(Nd,x), _(TM,R), _(TM,R),  _(M,R),  _(M,T),  _(x,x),  _(x,x),
-
-#define indic_offset_0x1780u 1496
-
-
-  /* Khmer */
-
-  /* 1780 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1788 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1790 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 1798 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 17A0 */  _(C,x),  _(C,x),  _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 17A8 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
-  /* 17B0 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x),  _(x,x),  _(M,R),  _(M,T),
-  /* 17B8 */  _(M,T),  _(M,T),  _(M,T),  _(M,B),  _(M,B),  _(M,B), _(M,TL),_(M,TLR),
-  /* 17C0 */ _(M,LR),  _(M,L),  _(M,L),  _(M,L), _(M,LR), _(M,LR), _(Bi,T), _(Vs,R),
-  /* 17C8 */  _(M,R), _(RS,T), _(RS,T), _(SM,T),_(CSR,T), _(CK,T), _(SM,T), _(SM,T),
-  /* 17D0 */ _(SM,T), _(PK,T), _(IS,x), _(SM,T),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 17D8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(A,x), _(SM,T),  _(x,x),  _(x,x),
-  /* 17E0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* 17E8 */ _(Nd,x), _(Nd,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-#define indic_offset_0x1cd0u 1608
-
-
-  /* Vedic Extensions */
-
-  /* 1CD0 */ _(Ca,T), _(Ca,T), _(Ca,T),  _(x,x), _(Ca,O), _(Ca,B), _(Ca,B), _(Ca,B),
-  /* 1CD8 */ _(Ca,B), _(Ca,B), _(Ca,T), _(Ca,T), _(Ca,B), _(Ca,B), _(Ca,B), _(Ca,B),
-  /* 1CE0 */ _(Ca,T), _(Ca,R),  _(x,O),  _(x,O),  _(x,O),  _(x,O),  _(x,O),  _(x,O),
-  /* 1CE8 */  _(x,O),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,B),  _(x,x),  _(x,x),
-  /* 1CF0 */  _(x,x),  _(x,x), _(CD,x), _(CD,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R),
-  /* 1CF8 */ _(Ca,x), _(Ca,x), _(CP,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-
-#define indic_offset_0x2008u 1656
-
-
-  /* General Punctuation */
-
-  /* 2008 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),_(ZWNJ,x),_(ZWJ,x),  _(x,x),  _(x,x),
-  /* 2010 */ _(CP,x), _(CP,x), _(CP,x), _(CP,x), _(CP,x),  _(x,x),  _(x,x),  _(x,x),
-
-#define indic_offset_0x2070u 1672
-
-
-  /* Superscripts and Subscripts */
-
-  /* 2070 */  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(SM,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 2078 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 2080 */  _(x,x),  _(x,x), _(SM,x), _(SM,x), _(SM,x),  _(x,x),  _(x,x),  _(x,x),
-
-#define indic_offset_0xa8e0u 1696
-
-
-  /* Devanagari Extended */
-
-  /* A8E0 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T),
-  /* A8E8 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T),
-  /* A8F0 */ _(Ca,T), _(Ca,T), _(Bi,x), _(Bi,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* A8F8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(VI,x),  _(M,T),
-
-#define indic_offset_0xa9e0u 1728
-
-
-  /* Myanmar Extended-B */
-
-  /* A9E0 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(M,T),  _(x,x),  _(C,x),
-  /* A9E8 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* A9F0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
-  /* A9F8 */ _(Nd,x), _(Nd,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(x,x),
-
-#define indic_offset_0xaa60u 1760
-
-
-  /* Myanmar Extended-A */
-
-  /* AA60 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* AA68 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* AA70 */  _(x,x),  _(C,x),  _(C,x),  _(C,x), _(CP,x), _(CP,x), _(CP,x),  _(x,x),
-  /* AA78 */  _(x,x),  _(x,x),  _(C,x), _(TM,R), _(TM,T), _(TM,R),  _(C,x),  _(C,x),
-
-}; /* Table items: 1792; occupancy: 71% */
-
-uint16_t
-hb_indic_get_categories (hb_codepoint_t u)
-{
-  switch (u >> 12)
-  {
-    case 0x0u:
-      if (unlikely (u == 0x00A0u)) return _(CP,x);
-      if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
-      if (hb_in_range<hb_codepoint_t> (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
-      if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0DF7u)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
-      break;
-
-    case 0x1u:
-      if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
-      if (hb_in_range<hb_codepoint_t> (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
-      if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
-      break;
-
-    case 0x2u:
-      if (unlikely (u == 0x25CCu)) return _(CP,x);
-      if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
-      if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
-      break;
-
-    case 0xAu:
-      if (hb_in_range<hb_codepoint_t> (u, 0xA8E0u, 0xA8FFu)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
-      if (hb_in_range<hb_codepoint_t> (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
-      if (hb_in_range<hb_codepoint_t> (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
-      break;
-
-    default:
-      break;
-  }
-  return _(x,x);
-}
-
-#undef _
-
-#undef ISC_A
-#undef ISC_Bi
-#undef ISC_BJN
-#undef ISC_Ca
-#undef ISC_C
-#undef ISC_CD
-#undef ISC_CF
-#undef ISC_CHL
-#undef ISC_CIP
-#undef ISC_CK
-#undef ISC_CM
-#undef ISC_CP
-#undef ISC_CPR
-#undef ISC_CPrf
-#undef ISC_CS
-#undef ISC_CSR
-#undef ISC_CWS
-#undef ISC_GM
-#undef ISC_IS
-#undef ISC_ZWJ
-#undef ISC_ML
-#undef ISC_ZWNJ
-#undef ISC_N
-#undef ISC_Nd
-#undef ISC_NJ
-#undef ISC_x
-#undef ISC_PK
-#undef ISC_RS
-#undef ISC_SM
-#undef ISC_TL
-#undef ISC_TM
-#undef ISC_V
-#undef ISC_Vs
-#undef ISC_Vo
-#undef ISC_M
-#undef ISC_VI
-
-#undef IMC_B
-#undef IMC_BL
-#undef IMC_BR
-#undef IMC_L
-#undef IMC_LR
-#undef IMC_x
-#undef IMC_O
-#undef IMC_R
-#undef IMC_T
-#undef IMC_TB
-#undef IMC_TBL
-#undef IMC_TBR
-#undef IMC_TL
-#undef IMC_TLR
-#undef IMC_TR
-#undef IMC_VOL
-
-#endif
-
-/* == End of generated table == */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic.hh
deleted file mode 100644
index da77a2887c4b4b9e6ead6c84c1b61deaa931294d..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic.hh
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright © 2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_INDIC_HH
-#define HB_OT_SHAPE_COMPLEX_INDIC_HH
-
-#include "hb.hh"
-
-#include "hb-ot-shape-complex-syllabic.hh"
-
-
-/* buffer var allocations */
-#define indic_category() complex_var_u8_category() /* indic_category_t */
-#define indic_position() complex_var_u8_auxiliary() /* indic_position_t */
-
-
-/* Cateories used in the OpenType spec:
- * https://docs.microsoft.com/en-us/typography/script-development/devanagari
- */
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum indic_category_t {
-  OT_X = 0,
-  OT_C = 1,
-  OT_V = 2,
-  OT_N = 3,
-  OT_H = 4,
-  OT_ZWNJ = 5,
-  OT_ZWJ = 6,
-  OT_M = 7,
-  OT_SM = 8,
-  /* OT_VD = 9, UNUSED; we use OT_A instead. */
-  OT_A = 10,
-  OT_PLACEHOLDER = 11,
-  OT_DOTTEDCIRCLE = 12,
-  OT_RS = 13, /* Register Shifter, used in Khmer OT spec. */
-  OT_Coeng = 14, /* Khmer-style Virama. */
-  OT_Repha = 15, /* Atomically-encoded logical or visual repha. */
-  OT_Ra = 16,
-  OT_CM = 17,  /* Consonant-Medial. */
-  OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */
-  OT_CS = 19,
-
-  /* The following are used by Khmer & Myanmar shapers.  Defined
-   * here for them to share. */
-  OT_VAbv    = 26,
-  OT_VBlw    = 27,
-  OT_VPre    = 28,
-  OT_VPst    = 29,
-};
-
-#define MEDIAL_FLAGS (FLAG (OT_CM))
-
-/* Note:
- *
- * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
- * cannot happen in a consonant syllable.  The plus side however is, we can call the
- * consonant syllable logic from the vowel syllable function and get it all right! */
-#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE))
-#define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ))
-
-
-/* Visual positions in a syllable from left to right. */
-enum indic_position_t {
-  POS_START = 0,
-
-  POS_RA_TO_BECOME_REPH = 1,
-  POS_PRE_M = 2,
-  POS_PRE_C = 3,
-
-  POS_BASE_C = 4,
-  POS_AFTER_MAIN = 5,
-
-  POS_ABOVE_C = 6,
-
-  POS_BEFORE_SUB = 7,
-  POS_BELOW_C = 8,
-  POS_AFTER_SUB = 9,
-
-  POS_BEFORE_POST = 10,
-  POS_POST_C = 11,
-  POS_AFTER_POST = 12,
-
-  POS_FINAL_C = 13,
-  POS_SMVD = 14,
-
-  POS_END = 15
-};
-
-/* Categories used in IndicSyllabicCategory.txt from UCD. */
-enum indic_syllabic_category_t {
-  INDIC_SYLLABIC_CATEGORY_OTHER				= OT_X,
-
-  INDIC_SYLLABIC_CATEGORY_AVAGRAHA			= OT_Symbol,
-  INDIC_SYLLABIC_CATEGORY_BINDU				= OT_SM,
-  INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER		= OT_PLACEHOLDER, /* Don't care. */
-  INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK		= OT_A,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT			= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD		= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL		= OT_CM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER		= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER		= OT_M, /* U+17CD only. */
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL		= OT_CM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER		= OT_PLACEHOLDER,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA	= OT_Repha,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED		= OT_X, /* Don't care. */
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED		= OT_CM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	= OT_CM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_CS,
-  INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		= OT_SM, /* https://github.com/harfbuzz/harfbuzz/issues/552 */
-  INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER		= OT_Coeng,
-  INDIC_SYLLABIC_CATEGORY_JOINER			= OT_ZWJ,
-  INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_NON_JOINER			= OT_ZWNJ,
-  INDIC_SYLLABIC_CATEGORY_NUKTA				= OT_N,
-  INDIC_SYLLABIC_CATEGORY_NUMBER			= OT_PLACEHOLDER,
-  INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER			= OT_PLACEHOLDER, /* Don't care. */
-  INDIC_SYLLABIC_CATEGORY_PURE_KILLER			= OT_M, /* Is like a vowel matra. */
-  INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER		= OT_RS,
-  INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER		= OT_SM,
-  INDIC_SYLLABIC_CATEGORY_TONE_LETTER			= OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_MARK			= OT_N,
-  INDIC_SYLLABIC_CATEGORY_VIRAMA			= OT_H,
-  INDIC_SYLLABIC_CATEGORY_VISARGA			= OT_SM,
-  INDIC_SYLLABIC_CATEGORY_VOWEL				= OT_V,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT		= OT_M,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT		= OT_V
-};
-
-/* Categories used in IndicSMatraCategory.txt from UCD */
-enum indic_matra_category_t {
-  INDIC_MATRA_CATEGORY_NOT_APPLICABLE			= POS_END,
-
-  INDIC_MATRA_CATEGORY_LEFT				= POS_PRE_C,
-  INDIC_MATRA_CATEGORY_TOP				= POS_ABOVE_C,
-  INDIC_MATRA_CATEGORY_BOTTOM				= POS_BELOW_C,
-  INDIC_MATRA_CATEGORY_RIGHT				= POS_POST_C,
-
-  /* These should resolve to the position of the last part of the split sequence. */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT			= INDIC_MATRA_CATEGORY_RIGHT,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT			= INDIC_MATRA_CATEGORY_RIGHT,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM			= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT		= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT		= INDIC_MATRA_CATEGORY_RIGHT,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT			= INDIC_MATRA_CATEGORY_TOP,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT		= INDIC_MATRA_CATEGORY_RIGHT,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT			= INDIC_MATRA_CATEGORY_RIGHT,
-
-  INDIC_MATRA_CATEGORY_OVERSTRUCK			= POS_AFTER_MAIN,
-  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT		= POS_PRE_M
-};
-
-#define INDIC_COMBINE_CATEGORIES(S,M) \
-  ( \
-    static_assert_expr (S < 255 && M < 255) + \
-    ( S | \
-     ( \
-      ( \
-       S == INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL || \
-       S == INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK || \
-       S == INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER || \
-       S == INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA || \
-       S == INDIC_SYLLABIC_CATEGORY_VIRAMA || \
-       S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT || \
-       false \
-       ? M : INDIC_MATRA_CATEGORY_NOT_APPLICABLE \
-      ) << 8 \
-     ) \
-    ) \
-   )
-
-HB_INTERNAL uint16_t
-hb_indic_get_categories (hb_codepoint_t u);
-
-
-static inline bool
-is_one_of (const hb_glyph_info_t &info, unsigned int flags)
-{
-  /* If it ligated, all bets are off. */
-  if (_hb_glyph_info_ligated (&info)) return false;
-  return !!(FLAG_UNSAFE (info.indic_category()) & flags);
-}
-
-static inline bool
-is_joiner (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, JOINER_FLAGS);
-}
-
-static inline bool
-is_consonant (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, CONSONANT_FLAGS);
-}
-
-static inline bool
-is_halant (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, FLAG (OT_H));
-}
-
-#define IN_HALF_BLOCK(u, Base) (((u) & ~0x7Fu) == (Base))
-
-#define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900u))
-#define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980u))
-#define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00u))
-#define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80u))
-#define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00u))
-#define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80u))
-#define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00u))
-#define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80u))
-#define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00u))
-#define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80u))
-
-
-#define MATRA_POS_LEFT(u)	POS_PRE_M
-#define MATRA_POS_RIGHT(u)	( \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_BENG(u) ? POS_AFTER_POST : \
-				  IS_GURU(u) ? POS_AFTER_POST : \
-				  IS_GUJR(u) ? POS_AFTER_POST : \
-				  IS_ORYA(u) ? POS_AFTER_POST : \
-				  IS_TAML(u) ? POS_AFTER_POST : \
-				  IS_TELU(u) ? (u <= 0x0C42u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
-				  IS_KNDA(u) ? (u < 0x0CC3u || u > 0xCD6u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
-				  IS_MLYM(u) ? POS_AFTER_POST : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-#define MATRA_POS_TOP(u)	( /* BENG and MLYM don't have top matras. */ \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \
-				  IS_GUJR(u) ? POS_AFTER_SUB  : \
-				  IS_ORYA(u) ? POS_AFTER_MAIN : \
-				  IS_TAML(u) ? POS_AFTER_SUB  : \
-				  IS_TELU(u) ? POS_BEFORE_SUB : \
-				  IS_KNDA(u) ? POS_BEFORE_SUB : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-#define MATRA_POS_BOTTOM(u)	( \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_BENG(u) ? POS_AFTER_SUB  : \
-				  IS_GURU(u) ? POS_AFTER_POST : \
-				  IS_GUJR(u) ? POS_AFTER_POST : \
-				  IS_ORYA(u) ? POS_AFTER_SUB  : \
-				  IS_TAML(u) ? POS_AFTER_POST : \
-				  IS_TELU(u) ? POS_BEFORE_SUB : \
-				  IS_KNDA(u) ? POS_BEFORE_SUB : \
-				  IS_MLYM(u) ? POS_AFTER_POST : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-
-static inline indic_position_t
-matra_position_indic (hb_codepoint_t u, indic_position_t side)
-{
-  switch ((int) side)
-  {
-    case POS_PRE_C:	return MATRA_POS_LEFT (u);
-    case POS_POST_C:	return MATRA_POS_RIGHT (u);
-    case POS_ABOVE_C:	return MATRA_POS_TOP (u);
-    case POS_BELOW_C:	return MATRA_POS_BOTTOM (u);
-  }
-  return side;
-}
-
-/* XXX
- * This is a hack for now.  We should move this data into the main Indic table.
- * Or completely remove it and just check in the tables.
- */
-static const hb_codepoint_t ra_chars[] = {
-  0x0930u, /* Devanagari */
-  0x09B0u, /* Bengali */
-  0x09F0u, /* Bengali */
-  0x0A30u, /* Gurmukhi */	/* No Reph */
-  0x0AB0u, /* Gujarati */
-  0x0B30u, /* Oriya */
-  0x0BB0u, /* Tamil */		/* No Reph */
-  0x0C30u, /* Telugu */		/* Reph formed only with ZWJ */
-  0x0CB0u, /* Kannada */
-  0x0D30u, /* Malayalam */	/* No Reph, Logical Repha */
-
-  0x0DBBu, /* Sinhala */	/* Reph formed only with ZWJ */
-};
-
-static inline bool
-is_ra (hb_codepoint_t u)
-{
-  return hb_array (ra_chars).lfind (u);
-}
-
-static inline void
-set_indic_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0xFFu);
-  indic_position_t pos = (indic_position_t) (type >> 8);
-
-
-  /*
-   * Re-assign category
-   */
-
-  /* The following act more like the Bindus. */
-  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0953u, 0x0954u)))
-    cat = OT_SM;
-  /* The following act like consonants. */
-  else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x0A72u, 0x0A73u,
-				      0x1CF5u, 0x1CF6u)))
-    cat = OT_C;
-  /* TODO: The following should only be allowed after a Visarga.
-   * For now, just treat them like regular tone marks. */
-  else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1CE2u, 0x1CE8u)))
-    cat = OT_A;
-  /* TODO: The following should only be allowed after some of
-   * the nasalization marks, maybe only for U+1CE9..U+1CF1.
-   * For now, just treat them like tone marks. */
-  else if (unlikely (u == 0x1CEDu))
-    cat = OT_A;
-  /* The following take marks in standalone clusters, similar to Avagraha. */
-  else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0xA8F2u, 0xA8F7u,
-				      0x1CE9u, 0x1CECu,
-				      0x1CEEu, 0x1CF1u)))
-  {
-    cat = OT_Symbol;
-    static_assert (((int) INDIC_SYLLABIC_CATEGORY_AVAGRAHA == OT_Symbol), "");
-  }
-  else if (unlikely (u == 0x0A51u))
-  {
-    /* https://github.com/harfbuzz/harfbuzz/issues/524 */
-    cat = OT_M;
-    pos = POS_BELOW_C;
-  }
-
-  /* According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil,
-   * so the Indic shaper needs to know their categories. */
-  else if (unlikely (u == 0x11301u || u == 0x11303u)) cat = OT_SM;
-  else if (unlikely (u == 0x1133Bu || u == 0x1133Cu)) cat = OT_N;
-
-  else if (unlikely (u == 0x0AFBu)) cat = OT_N; /* https://github.com/harfbuzz/harfbuzz/issues/552 */
-  else if (unlikely (u == 0x0B55u)) cat = OT_N; /* https://github.com/harfbuzz/harfbuzz/issues/2849 */
-
-  else if (unlikely (u == 0x0980u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/issues/538 */
-  else if (unlikely (u == 0x09FCu)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/1613 */
-  else if (unlikely (u == 0x0C80u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/623 */
-  else if (unlikely (u == 0x0D04u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/3511 */
-  else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x2010u, 0x2011u)))
-				    cat = OT_PLACEHOLDER;
-  else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE;
-
-
-  /*
-   * Re-assign position.
-   */
-
-  if ((FLAG_UNSAFE (cat) & CONSONANT_FLAGS))
-  {
-    pos = POS_BASE_C;
-    if (is_ra (u))
-      cat = OT_Ra;
-  }
-  else if (cat == OT_M)
-  {
-    pos = matra_position_indic (u, pos);
-  }
-  else if ((FLAG_UNSAFE (cat) & (FLAG (OT_SM) /* | FLAG (OT_VD) */ | FLAG (OT_A) | FLAG (OT_Symbol))))
-  {
-    pos = POS_SMVD;
-  }
-
-  if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
-
-
-
-  info.indic_category() = cat;
-  info.indic_position() = pos;
-}
-
-struct hb_indic_would_substitute_feature_t
-{
-  void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
-  {
-    zero_context = zero_context_;
-    map->get_stage_lookups (0/*GSUB*/,
-			    map->get_feature_stage (0/*GSUB*/, feature_tag),
-			    &lookups, &count);
-  }
-
-  bool would_substitute (const hb_codepoint_t *glyphs,
-			 unsigned int          glyphs_count,
-			 hb_face_t            *face) const
-  {
-    for (unsigned int i = 0; i < count; i++)
-      if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context))
-	return true;
-    return false;
-  }
-
-  private:
-  const hb_ot_map_t::lookup_map_t *lookups;
-  unsigned int count;
-  bool zero_context;
-};
-
-
-#endif /* HB_OT_SHAPE_COMPLEX_INDIC_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer-machine.hh
deleted file mode 100644
index c52f72f394e20c0d0915ae1469b0614f6656e30a..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer-machine.hh
+++ /dev/null
@@ -1,396 +0,0 @@
-
-#line 1 "hb-ot-shape-complex-khmer-machine.rl"
-/*
- * Copyright © 2011,2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
-
-#include "hb.hh"
-
-enum khmer_syllable_type_t {
-  khmer_consonant_syllable,
-  khmer_broken_cluster,
-  khmer_non_khmer_cluster,
-};
-
-
-#line 42 "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 60 "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
-};
-
-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
-};
-
-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
-};
-
-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 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 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 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 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 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 int khmer_syllable_machine_start = 20;
-static const int khmer_syllable_machine_first_final = 20;
-static const int khmer_syllable_machine_error = -1;
-
-static const int khmer_syllable_machine_en_main = 20;
-
-
-#line 43 "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) | 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 266 "hb-ot-shape-complex-khmer-machine.hh"
-	{
-	cs = khmer_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
-	}
-
-#line 106 "hb-ot-shape-complex-khmer-machine.rl"
-
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int syllable_serial = 1;
-  
-#line 282 "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:
-#line 1 "NONE"
-	{ts = p;}
-	break;
-#line 296 "hb-ot-shape-complex-khmer-machine.hh"
-	}
-
-	_keys = _khmer_syllable_machine_trans_keys + (cs<<1);
-	_inds = _khmer_syllable_machine_indicies + _khmer_syllable_machine_index_offsets[cs];
-
-	_slen = _khmer_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].khmer_category()) &&
-		( info[p].khmer_category()) <= _keys[1] ?
-		( info[p].khmer_category()) - _keys[0] : _slen ];
-
-_eof_trans:
-	cs = _khmer_syllable_machine_trans_targs[_trans];
-
-	if ( _khmer_syllable_machine_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _khmer_syllable_machine_trans_actions[_trans] ) {
-	case 2:
-#line 1 "NONE"
-	{te = p+1;}
-	break;
-	case 8:
-#line 82 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p+1;{ found_syllable (khmer_non_khmer_cluster); }}
-	break;
-	case 10:
-#line 80 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p;p--;{ found_syllable (khmer_consonant_syllable); }}
-	break;
-	case 12:
-#line 81 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p;p--;{ found_syllable (khmer_broken_cluster); }}
-	break;
-	case 11:
-#line 82 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p;p--;{ found_syllable (khmer_non_khmer_cluster); }}
-	break;
-	case 1:
-#line 80 "hb-ot-shape-complex-khmer-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (khmer_consonant_syllable); }}
-	break;
-	case 5:
-#line 81 "hb-ot-shape-complex-khmer-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (khmer_broken_cluster); }}
-	break;
-	case 3:
-#line 1 "NONE"
-	{	switch( act ) {
-	case 2:
-	{{p = ((te))-1;} found_syllable (khmer_broken_cluster); }
-	break;
-	case 3:
-	{{p = ((te))-1;} found_syllable (khmer_non_khmer_cluster); }
-	break;
-	}
-	}
-	break;
-	case 4:
-#line 1 "NONE"
-	{te = p+1;}
-#line 81 "hb-ot-shape-complex-khmer-machine.rl"
-	{act = 2;}
-	break;
-	case 9:
-#line 1 "NONE"
-	{te = p+1;}
-#line 82 "hb-ot-shape-complex-khmer-machine.rl"
-	{act = 3;}
-	break;
-#line 366 "hb-ot-shape-complex-khmer-machine.hh"
-	}
-
-_again:
-	switch ( _khmer_syllable_machine_to_state_actions[cs] ) {
-	case 6:
-#line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 375 "hb-ot-shape-complex-khmer-machine.hh"
-	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	if ( _khmer_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _khmer_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
-	}
-	}
-
-	}
-
-#line 114 "hb-ot-shape-complex-khmer-machine.rl"
-
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer.hh
deleted file mode 100644
index 35bfbb64d516bad5db431dc18e3d5a07a38d9d4c..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer.hh
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright © 2018  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_KHMER_HH
-#define HB_OT_SHAPE_COMPLEX_KHMER_HH
-
-#include "hb.hh"
-
-#include "hb-ot-shape-complex-indic.hh"
-
-
-/* buffer var allocations */
-#define khmer_category() indic_category() /* khmer_category_t */
-
-
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum khmer_category_t
-{
-  OT_Robatic = 20,
-  OT_Xgroup  = 21,
-  OT_Ygroup  = 22,
-  //OT_VAbv = 26,
-  //OT_VBlw = 27,
-  //OT_VPre = 28,
-  //OT_VPst = 29,
-};
-
-using khmer_position_t = indic_position_t;
-
-static inline void
-set_khmer_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  khmer_category_t cat = (khmer_category_t) (type & 0xFFu);
-  khmer_position_t pos = (khmer_position_t) (type >> 8);
-
-
-  /*
-   * Re-assign category
-   *
-   * These categories are experimentally extracted from what Uniscribe allows.
-   */
-  switch (u)
-  {
-    case 0x179Au:
-      cat = (khmer_category_t) OT_Ra;
-      break;
-
-    case 0x17CCu:
-    case 0x17C9u:
-    case 0x17CAu:
-      cat = OT_Robatic;
-      break;
-
-    case 0x17C6u:
-    case 0x17CBu:
-    case 0x17CDu:
-    case 0x17CEu:
-    case 0x17CFu:
-    case 0x17D0u:
-    case 0x17D1u:
-      cat = OT_Xgroup;
-      break;
-
-    case 0x17C7u:
-    case 0x17C8u:
-    case 0x17DDu:
-    case 0x17D3u: /* Just guessing. Uniscribe doesn't categorize it. */
-      cat = OT_Ygroup;
-      break;
-  }
-
-  /*
-   * Re-assign position.
-   */
-  if (cat == (khmer_category_t) OT_M)
-    switch ((int) pos)
-    {
-      case POS_PRE_C:	cat = (khmer_category_t) OT_VPre; break;
-      case POS_BELOW_C:	cat = (khmer_category_t) OT_VBlw; break;
-      case POS_ABOVE_C:	cat = (khmer_category_t) OT_VAbv; break;
-      case POS_POST_C:	cat = (khmer_category_t) OT_VPst; break;
-      default: assert (0);
-    }
-
-  info.khmer_category() = cat;
-}
-
-
-#endif /* HB_OT_SHAPE_COMPLEX_KHMER_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar-machine.hh
deleted file mode 100644
index f4ef33004d2038b6f729f1ca86a6ad27fc2f0219..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar-machine.hh
+++ /dev/null
@@ -1,492 +0,0 @@
-
-#line 1 "hb-ot-shape-complex-myanmar-machine.rl"
-/*
- * Copyright © 2011,2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
-
-#include "hb.hh"
-
-enum myanmar_syllable_type_t {
-  myanmar_consonant_syllable,
-  myanmar_punctuation_cluster,
-  myanmar_broken_cluster,
-  myanmar_non_myanmar_cluster,
-};
-
-
-#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_ML 33u
-#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 72 "hb-ot-shape-complex-myanmar-machine.hh"
-static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
-	1u, 33u, 3u, 33u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
-	3u, 33u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 33u, 1u, 16u, 3u, 33u, 3u, 33u, 
-	3u, 29u, 3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 33u, 3u, 33u, 3u, 33u, 
-	3u, 33u, 3u, 33u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
-	3u, 33u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 33u, 1u, 16u, 3u, 33u, 3u, 33u, 
-	3u, 33u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 33u, 3u, 33u, 
-	3u, 33u, 3u, 33u, 3u, 33u, 3u, 33u, 3u, 33u, 1u, 33u, 1u, 32u, 8u, 8u, 
-	0
-};
-
-static const char _myanmar_syllable_machine_key_spans[] = {
-	33, 31, 25, 4, 25, 23, 21, 21, 
-	31, 27, 27, 27, 31, 16, 31, 31, 
-	27, 27, 27, 28, 27, 31, 31, 31, 
-	31, 31, 25, 4, 25, 23, 21, 21, 
-	31, 27, 27, 27, 31, 16, 31, 31, 
-	31, 27, 27, 27, 28, 27, 31, 31, 
-	31, 31, 31, 31, 31, 33, 32, 1
-};
-
-static const short _myanmar_syllable_machine_index_offsets[] = {
-	0, 34, 66, 92, 97, 123, 147, 169, 
-	191, 223, 251, 279, 307, 339, 356, 388, 
-	420, 448, 476, 504, 533, 561, 593, 625, 
-	657, 689, 721, 747, 752, 778, 802, 824, 
-	846, 878, 906, 934, 962, 994, 1011, 1043, 
-	1075, 1107, 1135, 1163, 1191, 1220, 1248, 1280, 
-	1312, 1344, 1376, 1408, 1440, 1472, 1506, 1539
-};
-
-static const char _myanmar_syllable_machine_indicies[] = {
-	1, 1, 2, 3, 4, 4, 0, 5, 
-	0, 6, 1, 0, 0, 0, 0, 7, 
-	0, 8, 9, 0, 10, 11, 12, 13, 
-	14, 15, 16, 17, 18, 19, 20, 1, 
-	21, 0, 23, 24, 25, 25, 22, 26, 
-	22, 27, 22, 22, 22, 22, 22, 22, 
-	22, 28, 22, 22, 29, 30, 31, 32, 
-	33, 34, 35, 36, 37, 38, 22, 22, 
-	39, 22, 25, 25, 22, 26, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 40, 
-	22, 22, 22, 22, 22, 22, 33, 22, 
-	22, 22, 37, 22, 25, 25, 22, 26, 
-	22, 25, 25, 22, 26, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 33, 22, 22, 
-	22, 37, 22, 41, 22, 25, 25, 22, 
-	26, 22, 33, 22, 22, 22, 22, 22, 
-	22, 22, 42, 22, 22, 22, 22, 22, 
-	22, 33, 22, 25, 25, 22, 26, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	42, 22, 22, 22, 22, 22, 22, 33, 
-	22, 25, 25, 22, 26, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 33, 22, 23, 
-	22, 25, 25, 22, 26, 22, 27, 22, 
-	22, 22, 22, 22, 22, 22, 43, 22, 
-	22, 44, 22, 22, 22, 33, 45, 22, 
-	22, 37, 22, 22, 22, 43, 22, 23, 
-	22, 25, 25, 22, 26, 22, 27, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 33, 22, 22, 
-	22, 37, 22, 23, 22, 25, 25, 22, 
-	26, 22, 27, 22, 22, 22, 22, 22, 
-	22, 22, 43, 22, 22, 22, 22, 22, 
-	22, 33, 45, 22, 22, 37, 22, 23, 
-	22, 25, 25, 22, 26, 22, 27, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 33, 45, 22, 
-	22, 37, 22, 23, 22, 25, 25, 22, 
-	26, 22, 27, 22, 22, 22, 22, 22, 
-	22, 22, 43, 22, 22, 22, 22, 22, 
-	22, 33, 45, 22, 22, 37, 22, 22, 
-	22, 43, 22, 1, 1, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 1, 22, 23, 22, 25, 25, 
-	22, 26, 22, 27, 22, 22, 22, 22, 
-	22, 22, 22, 28, 22, 22, 29, 30, 
-	31, 32, 33, 34, 35, 36, 37, 22, 
-	22, 22, 39, 22, 23, 22, 25, 25, 
-	22, 26, 22, 27, 22, 22, 22, 22, 
-	22, 22, 22, 46, 22, 22, 22, 22, 
-	22, 22, 33, 34, 35, 36, 37, 22, 
-	22, 22, 39, 22, 23, 22, 25, 25, 
-	22, 26, 22, 27, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 33, 34, 35, 36, 37, 22, 
-	23, 22, 25, 25, 22, 26, 22, 27, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 33, 34, 
-	35, 22, 37, 22, 23, 22, 25, 25, 
-	22, 26, 22, 27, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 33, 22, 35, 22, 37, 22, 
-	23, 22, 25, 25, 22, 26, 22, 27, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 33, 34, 
-	35, 36, 37, 46, 22, 23, 22, 25, 
-	25, 22, 26, 22, 27, 22, 22, 22, 
-	22, 22, 22, 22, 46, 22, 22, 22, 
-	22, 22, 22, 33, 34, 35, 36, 37, 
-	22, 23, 22, 25, 25, 22, 26, 22, 
-	27, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 29, 22, 31, 22, 33, 
-	34, 35, 36, 37, 22, 22, 22, 39, 
-	22, 23, 22, 25, 25, 22, 26, 22, 
-	27, 22, 22, 22, 22, 22, 22, 22, 
-	46, 22, 22, 29, 22, 22, 22, 33, 
-	34, 35, 36, 37, 22, 22, 22, 39, 
-	22, 23, 22, 25, 25, 22, 26, 22, 
-	27, 22, 22, 22, 22, 22, 22, 22, 
-	47, 22, 22, 29, 30, 31, 22, 33, 
-	34, 35, 36, 37, 22, 22, 22, 39, 
-	22, 23, 22, 25, 25, 22, 26, 22, 
-	27, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 29, 30, 31, 22, 33, 
-	34, 35, 36, 37, 22, 22, 22, 39, 
-	22, 23, 24, 25, 25, 22, 26, 22, 
-	27, 22, 22, 22, 22, 22, 22, 22, 
-	28, 22, 22, 29, 30, 31, 32, 33, 
-	34, 35, 36, 37, 22, 22, 22, 39, 
-	22, 49, 49, 48, 5, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 50, 48, 
-	48, 48, 48, 48, 48, 14, 48, 48, 
-	48, 18, 48, 49, 49, 48, 5, 48, 
-	49, 49, 48, 5, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 14, 48, 48, 48, 
-	18, 48, 51, 48, 49, 49, 48, 5, 
-	48, 14, 48, 48, 48, 48, 48, 48, 
-	48, 52, 48, 48, 48, 48, 48, 48, 
-	14, 48, 49, 49, 48, 5, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 52, 
-	48, 48, 48, 48, 48, 48, 14, 48, 
-	49, 49, 48, 5, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 14, 48, 2, 48, 
-	49, 49, 48, 5, 48, 6, 48, 48, 
-	48, 48, 48, 48, 48, 53, 48, 48, 
-	54, 48, 48, 48, 14, 55, 48, 48, 
-	18, 48, 48, 48, 53, 48, 2, 48, 
-	49, 49, 48, 5, 48, 6, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 14, 48, 48, 48, 
-	18, 48, 2, 48, 49, 49, 48, 5, 
-	48, 6, 48, 48, 48, 48, 48, 48, 
-	48, 53, 48, 48, 48, 48, 48, 48, 
-	14, 55, 48, 48, 18, 48, 2, 48, 
-	49, 49, 48, 5, 48, 6, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 14, 55, 48, 48, 
-	18, 48, 2, 48, 49, 49, 48, 5, 
-	48, 6, 48, 48, 48, 48, 48, 48, 
-	48, 53, 48, 48, 48, 48, 48, 48, 
-	14, 55, 48, 48, 18, 48, 48, 48, 
-	53, 48, 56, 56, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 56, 48, 2, 3, 49, 49, 48, 
-	5, 48, 6, 48, 48, 48, 48, 48, 
-	48, 48, 8, 48, 48, 10, 11, 12, 
-	13, 14, 15, 16, 17, 18, 19, 48, 
-	48, 21, 48, 2, 48, 49, 49, 48, 
-	5, 48, 6, 48, 48, 48, 48, 48, 
-	48, 48, 8, 48, 48, 10, 11, 12, 
-	13, 14, 15, 16, 17, 18, 48, 48, 
-	48, 21, 48, 2, 48, 49, 49, 48, 
-	5, 48, 6, 48, 48, 48, 48, 48, 
-	48, 48, 57, 48, 48, 48, 48, 48, 
-	48, 14, 15, 16, 17, 18, 48, 48, 
-	48, 21, 48, 2, 48, 49, 49, 48, 
-	5, 48, 6, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 14, 15, 16, 17, 18, 48, 2, 
-	48, 49, 49, 48, 5, 48, 6, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 14, 15, 16, 
-	48, 18, 48, 2, 48, 49, 49, 48, 
-	5, 48, 6, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 14, 48, 16, 48, 18, 48, 2, 
-	48, 49, 49, 48, 5, 48, 6, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 14, 15, 16, 
-	17, 18, 57, 48, 2, 48, 49, 49, 
-	48, 5, 48, 6, 48, 48, 48, 48, 
-	48, 48, 48, 57, 48, 48, 48, 48, 
-	48, 48, 14, 15, 16, 17, 18, 48, 
-	2, 48, 49, 49, 48, 5, 48, 6, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 10, 48, 12, 48, 14, 15, 
-	16, 17, 18, 48, 48, 48, 21, 48, 
-	2, 48, 49, 49, 48, 5, 48, 6, 
-	48, 48, 48, 48, 48, 48, 48, 57, 
-	48, 48, 10, 48, 48, 48, 14, 15, 
-	16, 17, 18, 48, 48, 48, 21, 48, 
-	2, 48, 49, 49, 48, 5, 48, 6, 
-	48, 48, 48, 48, 48, 48, 48, 58, 
-	48, 48, 10, 11, 12, 48, 14, 15, 
-	16, 17, 18, 48, 48, 48, 21, 48, 
-	2, 48, 49, 49, 48, 5, 48, 6, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 10, 11, 12, 48, 14, 15, 
-	16, 17, 18, 48, 48, 48, 21, 48, 
-	2, 3, 49, 49, 48, 5, 48, 6, 
-	48, 48, 48, 48, 48, 48, 48, 8, 
-	48, 48, 10, 11, 12, 13, 14, 15, 
-	16, 17, 18, 48, 48, 48, 21, 48, 
-	23, 24, 25, 25, 22, 26, 22, 27, 
-	22, 22, 22, 22, 22, 22, 22, 59, 
-	22, 22, 29, 30, 31, 32, 33, 34, 
-	35, 36, 37, 38, 22, 22, 39, 22, 
-	23, 60, 25, 25, 22, 26, 22, 27, 
-	22, 22, 22, 22, 22, 22, 22, 28, 
-	22, 22, 29, 30, 31, 32, 33, 34, 
-	35, 36, 37, 22, 22, 22, 39, 22, 
-	1, 1, 2, 3, 49, 49, 48, 5, 
-	48, 6, 1, 48, 48, 48, 48, 1, 
-	48, 8, 48, 48, 10, 11, 12, 13, 
-	14, 15, 16, 17, 18, 19, 48, 1, 
-	21, 48, 1, 1, 61, 61, 61, 61, 
-	61, 61, 61, 61, 1, 61, 61, 61, 
-	61, 1, 61, 61, 61, 61, 61, 61, 
-	61, 61, 61, 61, 61, 61, 61, 61, 
-	61, 1, 61, 62, 61, 0
-};
-
-static const char _myanmar_syllable_machine_trans_targs[] = {
-	0, 1, 26, 37, 0, 27, 33, 51, 
-	39, 54, 40, 46, 47, 48, 29, 42, 
-	43, 44, 32, 50, 55, 45, 0, 2, 
-	13, 0, 3, 9, 14, 15, 21, 22, 
-	23, 5, 17, 18, 19, 8, 25, 20, 
-	4, 6, 7, 10, 12, 11, 16, 24, 
-	0, 0, 28, 30, 31, 34, 36, 35, 
-	38, 41, 49, 52, 53, 0, 0
-};
-
-static const char _myanmar_syllable_machine_trans_actions[] = {
-	3, 0, 0, 0, 4, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 5, 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, 
-	7, 8, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 9, 10
-};
-
-static const char _myanmar_syllable_machine_to_state_actions[] = {
-	1, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0
-};
-
-static const char _myanmar_syllable_machine_from_state_actions[] = {
-	2, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0
-};
-
-static const short _myanmar_syllable_machine_eof_trans[] = {
-	0, 23, 23, 23, 23, 23, 23, 23, 
-	23, 23, 23, 23, 23, 23, 23, 23, 
-	23, 23, 23, 23, 23, 23, 23, 23, 
-	23, 23, 49, 49, 49, 49, 49, 49, 
-	49, 49, 49, 49, 49, 49, 49, 49, 
-	49, 49, 49, 49, 49, 49, 49, 49, 
-	49, 49, 49, 23, 23, 49, 62, 62
-};
-
-static const int myanmar_syllable_machine_start = 0;
-static const int myanmar_syllable_machine_first_final = 0;
-static const int myanmar_syllable_machine_error = -1;
-
-static const int myanmar_syllable_machine_en_main = 0;
-
-
-#line 44 "hb-ot-shape-complex-myanmar-machine.rl"
-
-
-
-#line 102 "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) | syllable_type; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
-
-static void
-find_syllables_myanmar (hb_buffer_t *buffer)
-{
-  unsigned int p, pe, eof, ts, te, act HB_UNUSED;
-  int cs;
-  hb_glyph_info_t *info = buffer->info;
-  
-#line 382 "hb-ot-shape-complex-myanmar-machine.hh"
-	{
-	cs = myanmar_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
-	}
-
-#line 122 "hb-ot-shape-complex-myanmar-machine.rl"
-
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int syllable_serial = 1;
-  
-#line 398 "hb-ot-shape-complex-myanmar-machine.hh"
-	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-_resume:
-	switch ( _myanmar_syllable_machine_from_state_actions[cs] ) {
-	case 2:
-#line 1 "NONE"
-	{ts = p;}
-	break;
-#line 412 "hb-ot-shape-complex-myanmar-machine.hh"
-	}
-
-	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
-	_inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs];
-
-	_slen = _myanmar_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) &&
-		( info[p].myanmar_category()) <= _keys[1] ?
-		( info[p].myanmar_category()) - _keys[0] : _slen ];
-
-_eof_trans:
-	cs = _myanmar_syllable_machine_trans_targs[_trans];
-
-	if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
-	case 6:
-#line 94 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (myanmar_consonant_syllable); }}
-	break;
-	case 4:
-#line 95 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
-	break;
-	case 10:
-#line 96 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (myanmar_punctuation_cluster); }}
-	break;
-	case 8:
-#line 97 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (myanmar_broken_cluster); }}
-	break;
-	case 3:
-#line 98 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
-	break;
-	case 5:
-#line 94 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (myanmar_consonant_syllable); }}
-	break;
-	case 7:
-#line 97 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (myanmar_broken_cluster); }}
-	break;
-	case 9:
-#line 98 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }}
-	break;
-#line 462 "hb-ot-shape-complex-myanmar-machine.hh"
-	}
-
-_again:
-	switch ( _myanmar_syllable_machine_to_state_actions[cs] ) {
-	case 1:
-#line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 471 "hb-ot-shape-complex-myanmar-machine.hh"
-	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _myanmar_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
-	}
-	}
-
-	}
-
-#line 130 "hb-ot-shape-complex-myanmar-machine.rl"
-
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar.hh
deleted file mode 100644
index 7fbca3878f2ca5b0f05208d940e3b059c029f2b2..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar.hh
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright © 2018  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_HH
-#define HB_OT_SHAPE_COMPLEX_MYANMAR_HH
-
-#include "hb.hh"
-
-#include "hb-ot-shape-complex-indic.hh"
-
-
-/* buffer var allocations */
-#define myanmar_category() indic_category() /* myanmar_category_t */
-#define myanmar_position() indic_position() /* myanmar_position_t */
-
-
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum myanmar_category_t {
-  OT_As  = 18,  /* Asat */
-  OT_D0  = 20, /* Digit zero */
-  OT_DB  = OT_N, /* Dot below */
-  OT_GB  = OT_PLACEHOLDER,
-  OT_MH  = 21, /* Various consonant medial types */
-  OT_MR  = 22, /* Various consonant medial types */
-  OT_MW  = 23, /* Various consonant medial types */
-  OT_MY  = 24, /* Various consonant medial types */
-  OT_PT  = 25, /* Pwo and other tones */
-  //OT_VAbv = 26,
-  //OT_VBlw = 27,
-  //OT_VPre = 28,
-  //OT_VPst = 29,
-  OT_VS   = 30, /* Variation selectors */
-  OT_P    = 31, /* Punctuation */
-  OT_D    = 32, /* Digits except zero */
-  OT_ML   = 33, /* Various consonant medial types */
-};
-
-using myanmar_position_t = indic_position_t;
-
-static inline void
-set_myanmar_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  unsigned int cat = type & 0xFFu;
-  myanmar_position_t pos = (myanmar_position_t) (type >> 8);
-
-  /* Myanmar
-   * https://docs.microsoft.com/en-us/typography/script-development/myanmar#analyze
-   */
-  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)))
-    cat = OT_VS;
-
-  switch (u)
-  {
-    case 0x104Eu:
-      cat = OT_C; /* The spec says C, IndicSyllableCategory doesn't have. */
-      break;
-
-    case 0x002Du: case 0x00A0u: case 0x00D7u: case 0x2012u:
-    case 0x2013u: case 0x2014u: case 0x2015u: case 0x2022u:
-    case 0x25CCu: case 0x25FBu: case 0x25FCu: case 0x25FDu:
-    case 0x25FEu:
-      cat = OT_GB;
-      break;
-
-    case 0x1004u: case 0x101Bu: case 0x105Au:
-      cat = OT_Ra;
-      break;
-
-    case 0x1032u: case 0x1036u:
-      cat = OT_A;
-      break;
-
-    case 0x1039u:
-      cat = OT_H;
-      break;
-
-    case 0x103Au:
-      cat = OT_As;
-      break;
-
-    case 0x1041u: case 0x1042u: case 0x1043u: case 0x1044u:
-    case 0x1045u: case 0x1046u: case 0x1047u: case 0x1048u:
-    case 0x1049u: case 0x1090u: case 0x1091u: case 0x1092u:
-    case 0x1093u: case 0x1094u: case 0x1095u: case 0x1096u:
-    case 0x1097u: case 0x1098u: case 0x1099u:
-      cat = OT_D;
-      break;
-
-    case 0x1040u:
-      cat = OT_D; /* XXX The spec says D0, but Uniscribe doesn't seem to do. */
-      break;
-
-    case 0x103Eu:
-      cat = OT_MH;
-      break;
-
-    case 0x1060u:
-      cat = OT_ML;
-      break;
-
-    case 0x103Cu:
-      cat = OT_MR;
-      break;
-
-    case 0x103Du: case 0x1082u:
-      cat = OT_MW;
-      break;
-
-    case 0x103Bu: case 0x105Eu: case 0x105Fu:
-      cat = OT_MY;
-      break;
-
-    case 0x1063u: case 0x1064u: case 0x1069u: case 0x106Au:
-    case 0x106Bu: case 0x106Cu: case 0x106Du: case 0xAA7Bu:
-      cat = OT_PT;
-      break;
-
-    case 0x1038u: case 0x1087u: case 0x1088u: case 0x1089u:
-    case 0x108Au: case 0x108Bu: case 0x108Cu: case 0x108Du:
-    case 0x108Fu: case 0x109Au: case 0x109Bu: case 0x109Cu:
-      cat = OT_SM;
-      break;
-
-    case 0x104Au: case 0x104Bu:
-      cat = OT_P;
-      break;
-
-    case 0xAA74u: case 0xAA75u: case 0xAA76u:
-      /* https://github.com/harfbuzz/harfbuzz/issues/218 */
-      cat = OT_C;
-      break;
-  }
-
-  if (cat == OT_M)
-  {
-    switch ((int) pos)
-    {
-      case POS_PRE_C:	cat = (myanmar_category_t) OT_VPre;
-			pos = POS_PRE_M; break;
-      case POS_ABOVE_C:	cat = (myanmar_category_t) OT_VAbv;   break;
-      case POS_BELOW_C:	cat = (myanmar_category_t) OT_VBlw;   break;
-      case POS_POST_C:	cat = (myanmar_category_t) OT_VPst;   break;
-    }
-  }
-
-  info.myanmar_category() = cat;
-  info.myanmar_position() = pos;
-}
-
-
-#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-machine.hh
deleted file mode 100644
index 468bd95c3feb45fef74c18f082703623ccf19a42..0000000000000000000000000000000000000000
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-machine.hh
+++ /dev/null
@@ -1,849 +0,0 @@
-
-#line 1 "hb-ot-shape-complex-use-machine.rl"
-/*
- * Copyright © 2015  Mozilla Foundation.
- * Copyright © 2015  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Mozilla Author(s): Jonathan Kew
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
-
-#include "hb.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_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,
-};
-
-
-#line 57 "hb-ot-shape-complex-use-machine.hh"
-#define use_syllable_machine_ex_B 1u
-#define use_syllable_machine_ex_CGJ 6u
-#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_IS 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_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_WJ 16u
-#define use_syllable_machine_ex_ZWNJ 14u
-
-
-#line 100 "hb-ot-shape-complex-use-machine.hh"
-static const unsigned char _use_syllable_machine_trans_keys[] = {
-	0u, 51u, 11u, 48u, 11u, 48u, 1u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, 
-	45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, 22u, 48u, 
-	23u, 48u, 23u, 48u, 23u, 48u, 12u, 48u, 12u, 48u, 12u, 48u, 12u, 48u, 11u, 48u, 
-	1u, 1u, 11u, 48u, 41u, 42u, 42u, 42u, 11u, 48u, 11u, 48u, 1u, 48u, 23u, 48u, 
-	24u, 47u, 25u, 47u, 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 
-	1u, 1u, 24u, 48u, 22u, 48u, 23u, 48u, 23u, 48u, 23u, 48u, 12u, 48u, 12u, 48u, 
-	12u, 48u, 12u, 48u, 11u, 48u, 1u, 1u, 13u, 13u, 4u, 4u, 11u, 48u, 11u, 48u, 
-	1u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 
-	24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, 22u, 48u, 23u, 48u, 23u, 48u, 23u, 48u, 
-	12u, 48u, 12u, 48u, 12u, 48u, 12u, 48u, 11u, 48u, 1u, 1u, 11u, 48u, 11u, 48u, 
-	1u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 
-	24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, 22u, 48u, 23u, 48u, 23u, 48u, 23u, 48u, 
-	12u, 48u, 12u, 48u, 12u, 48u, 12u, 48u, 11u, 48u, 1u, 1u, 4u, 4u, 13u, 13u, 
-	1u, 48u, 11u, 48u, 41u, 42u, 42u, 42u, 1u, 5u, 50u, 52u, 49u, 52u, 49u, 51u, 
-	0
-};
-
-static const char _use_syllable_machine_key_spans[] = {
-	52, 38, 38, 48, 26, 24, 23, 22, 
-	2, 1, 25, 25, 25, 1, 25, 27, 
-	26, 26, 26, 37, 37, 37, 37, 38, 
-	1, 38, 2, 1, 38, 38, 48, 26, 
-	24, 23, 22, 2, 1, 25, 25, 25, 
-	1, 25, 27, 26, 26, 26, 37, 37, 
-	37, 37, 38, 1, 1, 1, 38, 38, 
-	48, 26, 24, 23, 22, 2, 1, 25, 
-	25, 25, 1, 25, 27, 26, 26, 26, 
-	37, 37, 37, 37, 38, 1, 38, 38, 
-	48, 26, 24, 23, 22, 2, 1, 25, 
-	25, 25, 1, 25, 27, 26, 26, 26, 
-	37, 37, 37, 37, 38, 1, 1, 1, 
-	48, 38, 2, 1, 5, 3, 4, 3
-};
-
-static const short _use_syllable_machine_index_offsets[] = {
-	0, 53, 92, 131, 180, 207, 232, 256, 
-	279, 282, 284, 310, 336, 362, 364, 390, 
-	418, 445, 472, 499, 537, 575, 613, 651, 
-	690, 692, 731, 734, 736, 775, 814, 863, 
-	890, 915, 939, 962, 965, 967, 993, 1019, 
-	1045, 1047, 1073, 1101, 1128, 1155, 1182, 1220, 
-	1258, 1296, 1334, 1373, 1375, 1377, 1379, 1418, 
-	1457, 1506, 1533, 1558, 1582, 1605, 1608, 1610, 
-	1636, 1662, 1688, 1690, 1716, 1744, 1771, 1798, 
-	1825, 1863, 1901, 1939, 1977, 2016, 2018, 2057, 
-	2096, 2145, 2172, 2197, 2221, 2244, 2247, 2249, 
-	2275, 2301, 2327, 2329, 2355, 2383, 2410, 2437, 
-	2464, 2502, 2540, 2578, 2616, 2655, 2657, 2659, 
-	2661, 2710, 2749, 2752, 2754, 2760, 2764, 2769
-};
-
-static const char _use_syllable_machine_indicies[] = {
-	0, 1, 2, 2, 3, 4, 2, 2, 
-	2, 2, 2, 5, 6, 7, 2, 2, 
-	2, 2, 8, 2, 2, 2, 9, 10, 
-	11, 12, 13, 14, 15, 16, 17, 18, 
-	19, 20, 21, 22, 2, 23, 24, 25, 
-	2, 26, 27, 28, 29, 30, 31, 32, 
-	29, 33, 2, 34, 2, 36, 37, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	38, 39, 40, 41, 42, 43, 44, 45, 
-	46, 47, 48, 49, 50, 51, 35, 52, 
-	53, 54, 35, 55, 56, 35, 57, 58, 
-	59, 60, 57, 35, 36, 37, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 38, 
-	39, 40, 41, 42, 43, 44, 45, 46, 
-	48, 48, 49, 50, 51, 35, 52, 53, 
-	54, 35, 35, 35, 35, 57, 58, 59, 
-	60, 57, 35, 36, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 39, 40, 41, 42, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 52, 
-	53, 54, 35, 35, 35, 35, 35, 58, 
-	59, 60, 61, 35, 39, 40, 41, 42, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 52, 53, 54, 35, 35, 35, 
-	35, 35, 58, 59, 60, 61, 35, 40, 
-	41, 42, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 58, 59, 60, 35, 
-	41, 42, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 58, 59, 60, 35, 
-	42, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 58, 59, 60, 35, 58, 
-	59, 35, 59, 35, 40, 41, 42, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 52, 53, 54, 35, 35, 35, 35, 
-	35, 58, 59, 60, 61, 35, 40, 41, 
-	42, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 53, 54, 35, 35, 
-	35, 35, 35, 58, 59, 60, 61, 35, 
-	40, 41, 42, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 54, 
-	35, 35, 35, 35, 35, 58, 59, 60, 
-	61, 35, 62, 35, 40, 41, 42, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 58, 59, 60, 61, 35, 38, 39, 
-	40, 41, 42, 35, 35, 35, 35, 35, 
-	35, 49, 50, 51, 35, 52, 53, 54, 
-	35, 35, 35, 35, 35, 58, 59, 60, 
-	61, 35, 39, 40, 41, 42, 35, 35, 
-	35, 35, 35, 35, 49, 50, 51, 35, 
-	52, 53, 54, 35, 35, 35, 35, 35, 
-	58, 59, 60, 61, 35, 39, 40, 41, 
-	42, 35, 35, 35, 35, 35, 35, 35, 
-	50, 51, 35, 52, 53, 54, 35, 35, 
-	35, 35, 35, 58, 59, 60, 61, 35, 
-	39, 40, 41, 42, 35, 35, 35, 35, 
-	35, 35, 35, 35, 51, 35, 52, 53, 
-	54, 35, 35, 35, 35, 35, 58, 59, 
-	60, 61, 35, 39, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 38, 39, 40, 
-	41, 42, 35, 44, 45, 35, 35, 35, 
-	49, 50, 51, 35, 52, 53, 54, 35, 
-	35, 35, 35, 35, 58, 59, 60, 61, 
-	35, 39, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 38, 39, 40, 41, 42, 
-	35, 35, 45, 35, 35, 35, 49, 50, 
-	51, 35, 52, 53, 54, 35, 35, 35, 
-	35, 35, 58, 59, 60, 61, 35, 39, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 38, 39, 40, 41, 42, 35, 35, 
-	35, 35, 35, 35, 49, 50, 51, 35, 
-	52, 53, 54, 35, 35, 35, 35, 35, 
-	58, 59, 60, 61, 35, 39, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 38, 
-	39, 40, 41, 42, 43, 44, 45, 35, 
-	35, 35, 49, 50, 51, 35, 52, 53, 
-	54, 35, 35, 35, 35, 35, 58, 59, 
-	60, 61, 35, 36, 37, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 38, 39, 
-	40, 41, 42, 43, 44, 45, 46, 35, 
-	48, 49, 50, 51, 35, 52, 53, 54, 
-	35, 35, 35, 35, 57, 58, 59, 60, 
-	57, 35, 36, 35, 36, 37, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 38, 
-	39, 40, 41, 42, 43, 44, 45, 46, 
-	47, 48, 49, 50, 51, 35, 52, 53, 
-	54, 35, 35, 35, 35, 57, 58, 59, 
-	60, 57, 35, 55, 56, 35, 56, 35, 
-	64, 65, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 66, 67, 68, 69, 70, 
-	71, 72, 73, 74, 1, 75, 76, 77, 
-	78, 63, 79, 80, 81, 63, 63, 63, 
-	63, 82, 83, 84, 85, 86, 63, 64, 
-	65, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 66, 67, 68, 69, 70, 71, 
-	72, 73, 74, 75, 75, 76, 77, 78, 
-	63, 79, 80, 81, 63, 63, 63, 63, 
-	82, 83, 84, 85, 86, 63, 64, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 67, 68, 69, 70, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 79, 80, 81, 63, 63, 63, 
-	63, 63, 83, 84, 85, 87, 63, 67, 
-	68, 69, 70, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 79, 80, 81, 
-	63, 63, 63, 63, 63, 83, 84, 85, 
-	87, 63, 68, 69, 70, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 83, 
-	84, 85, 63, 69, 70, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 83, 
-	84, 85, 63, 70, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 83, 84, 
-	85, 63, 83, 84, 63, 84, 63, 68, 
-	69, 70, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 79, 80, 81, 63, 
-	63, 63, 63, 63, 83, 84, 85, 87, 
-	63, 68, 69, 70, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 80, 
-	81, 63, 63, 63, 63, 63, 83, 84, 
-	85, 87, 63, 68, 69, 70, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 81, 63, 63, 63, 63, 63, 
-	83, 84, 85, 87, 63, 89, 88, 68, 
-	69, 70, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 83, 84, 85, 87, 
-	63, 66, 67, 68, 69, 70, 63, 63, 
-	63, 63, 63, 63, 76, 77, 78, 63, 
-	79, 80, 81, 63, 63, 63, 63, 63, 
-	83, 84, 85, 87, 63, 67, 68, 69, 
-	70, 63, 63, 63, 63, 63, 63, 76, 
-	77, 78, 63, 79, 80, 81, 63, 63, 
-	63, 63, 63, 83, 84, 85, 87, 63, 
-	67, 68, 69, 70, 63, 63, 63, 63, 
-	63, 63, 63, 77, 78, 63, 79, 80, 
-	81, 63, 63, 63, 63, 63, 83, 84, 
-	85, 87, 63, 67, 68, 69, 70, 63, 
-	63, 63, 63, 63, 63, 63, 63, 78, 
-	63, 79, 80, 81, 63, 63, 63, 63, 
-	63, 83, 84, 85, 87, 63, 67, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	66, 67, 68, 69, 70, 63, 72, 73, 
-	63, 63, 63, 76, 77, 78, 63, 79, 
-	80, 81, 63, 63, 63, 63, 63, 83, 
-	84, 85, 87, 63, 67, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 66, 67, 
-	68, 69, 70, 63, 63, 73, 63, 63, 
-	63, 76, 77, 78, 63, 79, 80, 81, 
-	63, 63, 63, 63, 63, 83, 84, 85, 
-	87, 63, 67, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 66, 67, 68, 69, 
-	70, 63, 63, 63, 63, 63, 63, 76, 
-	77, 78, 63, 79, 80, 81, 63, 63, 
-	63, 63, 63, 83, 84, 85, 87, 63, 
-	67, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 66, 67, 68, 69, 70, 71, 
-	72, 73, 63, 63, 63, 76, 77, 78, 
-	63, 79, 80, 81, 63, 63, 63, 63, 
-	63, 83, 84, 85, 87, 63, 64, 65, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 66, 67, 68, 69, 70, 71, 72, 
-	73, 74, 63, 75, 76, 77, 78, 63, 
-	79, 80, 81, 63, 63, 63, 63, 82, 
-	83, 84, 85, 86, 63, 64, 90, 92, 
-	91, 3, 93, 94, 95, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 96, 97, 
-	98, 99, 100, 101, 102, 103, 104, 105, 
-	106, 107, 108, 109, 63, 110, 111, 112, 
-	63, 55, 56, 63, 113, 114, 115, 85, 
-	116, 63, 94, 95, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 96, 97, 98, 
-	99, 100, 101, 102, 103, 104, 106, 106, 
-	107, 108, 109, 63, 110, 111, 112, 63, 
-	63, 63, 63, 113, 114, 115, 85, 116, 
-	63, 94, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 97, 
-	98, 99, 100, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 110, 111, 112, 
-	63, 63, 63, 63, 63, 114, 115, 85, 
-	117, 63, 97, 98, 99, 100, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	110, 111, 112, 63, 63, 63, 63, 63, 
-	114, 115, 85, 117, 63, 98, 99, 100, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 114, 115, 85, 63, 99, 100, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 114, 115, 85, 63, 100, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 114, 115, 85, 63, 114, 115, 63, 
-	115, 63, 98, 99, 100, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 110, 
-	111, 112, 63, 63, 63, 63, 63, 114, 
-	115, 85, 117, 63, 98, 99, 100, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 111, 112, 63, 63, 63, 63, 
-	63, 114, 115, 85, 117, 63, 98, 99, 
-	100, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 112, 63, 63, 
-	63, 63, 63, 114, 115, 85, 117, 63, 
-	118, 88, 98, 99, 100, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 114, 
-	115, 85, 117, 63, 96, 97, 98, 99, 
-	100, 63, 63, 63, 63, 63, 63, 107, 
-	108, 109, 63, 110, 111, 112, 63, 63, 
-	63, 63, 63, 114, 115, 85, 117, 63, 
-	97, 98, 99, 100, 63, 63, 63, 63, 
-	63, 63, 107, 108, 109, 63, 110, 111, 
-	112, 63, 63, 63, 63, 63, 114, 115, 
-	85, 117, 63, 97, 98, 99, 100, 63, 
-	63, 63, 63, 63, 63, 63, 108, 109, 
-	63, 110, 111, 112, 63, 63, 63, 63, 
-	63, 114, 115, 85, 117, 63, 97, 98, 
-	99, 100, 63, 63, 63, 63, 63, 63, 
-	63, 63, 109, 63, 110, 111, 112, 63, 
-	63, 63, 63, 63, 114, 115, 85, 117, 
-	63, 97, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 96, 97, 98, 99, 100, 
-	63, 102, 103, 63, 63, 63, 107, 108, 
-	109, 63, 110, 111, 112, 63, 63, 63, 
-	63, 63, 114, 115, 85, 117, 63, 97, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 96, 97, 98, 99, 100, 63, 63, 
-	103, 63, 63, 63, 107, 108, 109, 63, 
-	110, 111, 112, 63, 63, 63, 63, 63, 
-	114, 115, 85, 117, 63, 97, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 96, 
-	97, 98, 99, 100, 63, 63, 63, 63, 
-	63, 63, 107, 108, 109, 63, 110, 111, 
-	112, 63, 63, 63, 63, 63, 114, 115, 
-	85, 117, 63, 97, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 96, 97, 98, 
-	99, 100, 101, 102, 103, 63, 63, 63, 
-	107, 108, 109, 63, 110, 111, 112, 63, 
-	63, 63, 63, 63, 114, 115, 85, 117, 
-	63, 94, 95, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 96, 97, 98, 99, 
-	100, 101, 102, 103, 104, 63, 106, 107, 
-	108, 109, 63, 110, 111, 112, 63, 63, 
-	63, 63, 113, 114, 115, 85, 116, 63, 
-	94, 90, 94, 95, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 96, 97, 98, 
-	99, 100, 101, 102, 103, 104, 105, 106, 
-	107, 108, 109, 63, 110, 111, 112, 63, 
-	63, 63, 63, 113, 114, 115, 85, 116, 
-	63, 5, 6, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 9, 10, 11, 12, 
-	13, 14, 15, 16, 17, 19, 19, 20, 
-	21, 22, 119, 23, 24, 25, 119, 119, 
-	119, 119, 29, 30, 31, 32, 29, 119, 
-	5, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 10, 11, 
-	12, 13, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 23, 24, 25, 119, 
-	119, 119, 119, 119, 30, 31, 32, 120, 
-	119, 10, 11, 12, 13, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 23, 
-	24, 25, 119, 119, 119, 119, 119, 30, 
-	31, 32, 120, 119, 11, 12, 13, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 30, 31, 32, 119, 12, 13, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 30, 31, 32, 119, 13, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	30, 31, 32, 119, 30, 31, 119, 31, 
-	119, 11, 12, 13, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 23, 24, 
-	25, 119, 119, 119, 119, 119, 30, 31, 
-	32, 120, 119, 11, 12, 13, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 24, 25, 119, 119, 119, 119, 119, 
-	30, 31, 32, 120, 119, 11, 12, 13, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 25, 119, 119, 119, 
-	119, 119, 30, 31, 32, 120, 119, 121, 
-	119, 11, 12, 13, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 30, 31, 
-	32, 120, 119, 9, 10, 11, 12, 13, 
-	119, 119, 119, 119, 119, 119, 20, 21, 
-	22, 119, 23, 24, 25, 119, 119, 119, 
-	119, 119, 30, 31, 32, 120, 119, 10, 
-	11, 12, 13, 119, 119, 119, 119, 119, 
-	119, 20, 21, 22, 119, 23, 24, 25, 
-	119, 119, 119, 119, 119, 30, 31, 32, 
-	120, 119, 10, 11, 12, 13, 119, 119, 
-	119, 119, 119, 119, 119, 21, 22, 119, 
-	23, 24, 25, 119, 119, 119, 119, 119, 
-	30, 31, 32, 120, 119, 10, 11, 12, 
-	13, 119, 119, 119, 119, 119, 119, 119, 
-	119, 22, 119, 23, 24, 25, 119, 119, 
-	119, 119, 119, 30, 31, 32, 120, 119, 
-	10, 119, 119, 119, 119, 119, 119, 119, 
-	119, 119, 9, 10, 11, 12, 13, 119, 
-	15, 16, 119, 119, 119, 20, 21, 22, 
-	119, 23, 24, 25, 119, 119, 119, 119, 
-	119, 30, 31, 32, 120, 119, 10, 119, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	9, 10, 11, 12, 13, 119, 119, 16, 
-	119, 119, 119, 20, 21, 22, 119, 23, 
-	24, 25, 119, 119, 119, 119, 119, 30, 
-	31, 32, 120, 119, 10, 119, 119, 119, 
-	119, 119, 119, 119, 119, 119, 9, 10, 
-	11, 12, 13, 119, 119, 119, 119, 119, 
-	119, 20, 21, 22, 119, 23, 24, 25, 
-	119, 119, 119, 119, 119, 30, 31, 32, 
-	120, 119, 10, 119, 119, 119, 119, 119, 
-	119, 119, 119, 119, 9, 10, 11, 12, 
-	13, 14, 15, 16, 119, 119, 119, 20, 
-	21, 22, 119, 23, 24, 25, 119, 119, 
-	119, 119, 119, 30, 31, 32, 120, 119, 
-	5, 6, 119, 119, 119, 119, 119, 119, 
-	119, 119, 119, 9, 10, 11, 12, 13, 
-	14, 15, 16, 17, 119, 19, 20, 21, 
-	22, 119, 23, 24, 25, 119, 119, 119, 
-	119, 29, 30, 31, 32, 29, 119, 5, 
-	119, 122, 119, 7, 119, 1, 119, 119, 
-	119, 1, 119, 119, 119, 119, 119, 5, 
-	6, 7, 119, 119, 119, 119, 119, 119, 
-	119, 119, 9, 10, 11, 12, 13, 14, 
-	15, 16, 17, 18, 19, 20, 21, 22, 
-	119, 23, 24, 25, 119, 26, 27, 119, 
-	29, 30, 31, 32, 29, 119, 5, 6, 
-	119, 119, 119, 119, 119, 119, 119, 119, 
-	119, 9, 10, 11, 12, 13, 14, 15, 
-	16, 17, 18, 19, 20, 21, 22, 119, 
-	23, 24, 25, 119, 119, 119, 119, 29, 
-	30, 31, 32, 29, 119, 26, 27, 119, 
-	27, 119, 1, 123, 123, 123, 1, 123, 
-	125, 124, 33, 124, 33, 125, 124, 125, 
-	124, 33, 124, 34, 124, 0
-};
-
-static const char _use_syllable_machine_trans_targs[] = {
-	1, 28, 0, 52, 54, 79, 80, 102, 
-	104, 92, 81, 82, 83, 84, 96, 97, 
-	98, 99, 105, 100, 93, 94, 95, 87, 
-	88, 89, 106, 107, 108, 101, 85, 86, 
-	0, 109, 111, 0, 2, 3, 15, 4, 
-	5, 6, 7, 19, 20, 21, 22, 25, 
-	23, 16, 17, 18, 10, 11, 12, 26, 
-	27, 24, 8, 9, 0, 13, 14, 0, 
-	29, 30, 42, 31, 32, 33, 34, 46, 
-	47, 48, 49, 50, 43, 44, 45, 37, 
-	38, 39, 51, 35, 36, 0, 51, 40, 
-	0, 41, 0, 0, 53, 0, 55, 56, 
-	68, 57, 58, 59, 60, 72, 73, 74, 
-	75, 78, 76, 69, 70, 71, 63, 64, 
-	65, 77, 61, 62, 77, 66, 67, 0, 
-	90, 91, 103, 0, 0, 110
-};
-
-static const char _use_syllable_machine_trans_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, 
-	4, 0, 0, 5, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 6, 0, 0, 7, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 8, 0, 0, 9, 10, 0, 
-	11, 0, 12, 13, 0, 14, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 8, 0, 0, 10, 0, 0, 15, 
-	0, 0, 0, 16, 17, 0
-};
-
-static const char _use_syllable_machine_to_state_actions[] = {
-	1, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0
-};
-
-static const char _use_syllable_machine_from_state_actions[] = {
-	2, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	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[] = {
-	0, 36, 36, 36, 36, 36, 36, 36, 
-	36, 36, 36, 36, 36, 36, 36, 36, 
-	36, 36, 36, 36, 36, 36, 36, 36, 
-	36, 36, 36, 36, 64, 64, 64, 64, 
-	64, 64, 64, 64, 64, 64, 64, 64, 
-	89, 64, 64, 64, 64, 64, 64, 64, 
-	64, 64, 64, 91, 92, 94, 64, 64, 
-	64, 64, 64, 64, 64, 64, 64, 64, 
-	64, 64, 89, 64, 64, 64, 64, 64, 
-	64, 64, 64, 64, 64, 91, 64, 120, 
-	120, 120, 120, 120, 120, 120, 120, 120, 
-	120, 120, 120, 120, 120, 120, 120, 120, 
-	120, 120, 120, 120, 120, 120, 120, 120, 
-	120, 120, 120, 120, 124, 125, 125, 125
-};
-
-static const int use_syllable_machine_start = 0;
-static const int use_syllable_machine_first_final = 0;
-static const int use_syllable_machine_error = -1;
-
-static const int use_syllable_machine_en_main = 0;
-
-
-#line 58 "hb-ot-shape-complex-use-machine.rl"
-
-
-
-#line 181 "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) | 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) : hb_iter_with_fallback_t<machine_index_t<Iter>,
-								       typename Iter::item_t> (),
-					       it (o.it), is_null (o.is_null) {}
-
-  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)
-  {
-    assert (n == 0);
-    is_null = true;
-  }
-  explicit operator bool () { return !is_null; }
-
-  void operator = (const machine_index_t& o)
-  {
-    is_null = o.is_null;
-    unsigned index = (*it).first;
-    unsigned n = (*o.it).first;
-    if (index < n) it += n - index; else if (index > n) it -= index - n;
-  }
-  bool operator == (const machine_index_t& o) const
-  { return is_null ? o.is_null : !o.is_null && (*it).first == (*o.it).first; }
-  bool operator != (const machine_index_t& o) const { return !(*this == o); }
-
-  private:
-  Iter it;
-  bool is_null = false;
-};
-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_ccs_default_ignorable (const hb_glyph_info_t &i)
-{ return i.use_category() != USE(CGJ); }
-
-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_ccs_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_ccs_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 702 "hb-ot-shape-complex-use-machine.hh"
-	{
-	cs = use_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
-	}
-
-#line 281 "hb-ot-shape-complex-use-machine.rl"
-
-
-  unsigned int syllable_serial = 1;
-  
-#line 715 "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 2:
-#line 1 "NONE"
-	{ts = p;}
-	break;
-#line 729 "hb-ot-shape-complex-use-machine.hh"
-	}
-
-	_keys = _use_syllable_machine_trans_keys + (cs<<1);
-	_inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs];
-
-	_slen = _use_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( (*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 9:
-#line 171 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (use_standard_cluster); }}
-	break;
-	case 6:
-#line 174 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (use_symbol_cluster); }}
-	break;
-	case 4:
-#line 176 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (use_broken_cluster); }}
-	break;
-	case 3:
-#line 177 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (use_non_cluster); }}
-	break;
-	case 11:
-#line 170 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_sakot_terminated_cluster); }}
-	break;
-	case 7:
-#line 171 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_standard_cluster); }}
-	break;
-	case 14:
-#line 172 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_number_joiner_terminated_cluster); }}
-	break;
-	case 13:
-#line 173 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_numeral_cluster); }}
-	break;
-	case 5:
-#line 174 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_symbol_cluster); }}
-	break;
-	case 17:
-#line 175 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_hieroglyph_cluster); }}
-	break;
-	case 15:
-#line 176 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_broken_cluster); }}
-	break;
-	case 16:
-#line 177 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (use_non_cluster); }}
-	break;
-	case 12:
-#line 1 "NONE"
-	{	switch( act ) {
-	case 1:
-	{{p = ((te))-1;} found_syllable (use_virama_terminated_cluster); }
-	break;
-	case 2:
-	{{p = ((te))-1;} found_syllable (use_sakot_terminated_cluster); }
-	break;
-	}
-	}
-	break;
-	case 8:
-#line 1 "NONE"
-	{te = p+1;}
-#line 169 "hb-ot-shape-complex-use-machine.rl"
-	{act = 1;}
-	break;
-	case 10:
-#line 1 "NONE"
-	{te = p+1;}
-#line 170 "hb-ot-shape-complex-use-machine.rl"
-	{act = 2;}
-	break;
-#line 819 "hb-ot-shape-complex-use-machine.hh"
-	}
-
-_again:
-	switch ( _use_syllable_machine_to_state_actions[cs] ) {
-	case 1:
-#line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 828 "hb-ot-shape-complex-use-machine.hh"
-	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _use_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
-	}
-	}
-
-	}
-
-#line 286 "hb-ot-shape-complex-use-machine.rl"
-
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-normalize.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-normalize.cc
index aa5a8eeaa3dfb1f752b9354ce338570d307ad402..7db0b25b731544d20eb8e272e0759123cf37f95b 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-normalize.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-normalize.cc
@@ -29,7 +29,7 @@
 #ifndef HB_NO_OT_SHAPE
 
 #include "hb-ot-shape-normalize.hh"
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 #include "hb-ot-shape.hh"
 
 
@@ -69,7 +69,7 @@
  *   - When a font does not support a character but supports its canonical
  *     decomposition, well, use the decomposition.
  *
- *   - The complex shapers can customize the compose and decompose functions to
+ *   - The shapers can customize the compose and decompose functions to
  *     offload some of their requirements to the normalizer.  For example, the
  *     Indic shaper may want to disallow recomposing of two matras.
  */
@@ -143,8 +143,7 @@ decompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint
     return 1;
   }
 
-  unsigned int ret;
-  if ((ret = decompose (c, shortest, a))) {
+  if (unsigned ret = decompose (c, shortest, a)) {
     if (b) {
       output_char (buffer, b, b_glyph);
       return ret + 1;
@@ -223,7 +222,7 @@ handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c,
 				   unsigned int end,
 				   bool short_circuit HB_UNUSED)
 {
-  /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
+  /* Currently if there's a variation-selector we give-up on normalization, it's just too hard. */
   hb_buffer_t * const buffer = c->buffer;
   hb_font_t * const font = c->font;
   for (; buffer->idx < end - 1 && buffer->successful;) {
@@ -395,7 +394,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
 	  break;
 
       /* We are going to do a O(n^2).  Only do this if the sequence is short. */
-      if (end - i > HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS) {
+      if (end - i > HB_OT_SHAPE_MAX_COMBINING_MARKS) {
 	i = end;
 	continue;
       }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.cc
index 99aadab3f0317419e5f7bcc724aa6019e9a412cd..0806abb7dd39fe579dc4083e74972972038c2e89 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.cc
@@ -37,7 +37,7 @@
 #include "hb-shaper-impl.hh"
 
 #include "hb-ot-shape.hh"
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 #include "hb-ot-shape-fallback.hh"
 #include "hb-ot-shape-normalize.hh"
 
@@ -86,14 +86,14 @@ hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t                     *fac
 						, apply_morx (_hb_apply_morx (face, props))
 #endif
 {
-  shaper = hb_ot_shape_complex_categorize (this);
+  shaper = hb_ot_shaper_categorize (this);
 
   script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
   script_fallback_mark_positioning = shaper->fallback_position;
 
   /* https://github.com/harfbuzz/harfbuzz/issues/1528 */
-  if (apply_morx && shaper != &_hb_ot_complex_shaper_default)
-    shaper = &_hb_ot_complex_shaper_dumber;
+  if (apply_morx && shaper != &_hb_ot_shaper_default)
+    shaper = &_hb_ot_shaper_dumber;
 }
 
 void
@@ -927,7 +927,7 @@ hb_ot_substitute_default (const hb_ot_shape_context_t *c)
 }
 
 static inline void
-hb_ot_substitute_complex (const hb_ot_shape_context_t *c)
+hb_ot_substitute_plan (const hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
 
@@ -946,7 +946,7 @@ hb_ot_substitute_pre (const hb_ot_shape_context_t *c)
 
   _hb_buffer_allocate_gsubgpos_vars (c->buffer);
 
-  hb_ot_substitute_complex (c);
+  hb_ot_substitute_plan (c);
 
 #ifndef HB_NO_AAT_SHAPE
   if (c->plan->apply_morx && c->plan->apply_gpos)
@@ -1039,7 +1039,7 @@ hb_ot_position_default (const hb_ot_shape_context_t *c)
 }
 
 static inline void
-hb_ot_position_complex (const hb_ot_shape_context_t *c)
+hb_ot_position_plan (const hb_ot_shape_context_t *c)
 {
   unsigned int count = c->buffer->len;
   hb_glyph_info_t *info = c->buffer->info;
@@ -1124,7 +1124,7 @@ hb_ot_position (const hb_ot_shape_context_t *c)
 
   hb_ot_position_default (c);
 
-  hb_ot_position_complex (c);
+  hb_ot_position_plan (c);
 
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
     hb_buffer_reverse (c->buffer);
@@ -1159,8 +1159,6 @@ hb_propagate_flags (hb_buffer_t *buffer)
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
-  c->buffer->enter ();
-
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.hh
index e8c81015c792ea52b53cb9636aae4cabcc316ccf..17fa58b3377a5772ab40f2f0d1aa3cbeab9c32cd 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.hh
@@ -61,7 +61,7 @@ struct hb_shape_plan_key_t;
 struct hb_ot_shape_plan_t
 {
   hb_segment_properties_t props;
-  const struct hb_ot_complex_shaper_t *shaper;
+  const struct hb_ot_shaper_t *shaper;
   hb_ot_map_t map;
   hb_aat_map_t aat_map;
   const void *data;
@@ -158,7 +158,7 @@ struct hb_ot_shape_planner_t
 #endif
   bool script_zero_marks : 1;
   bool script_fallback_mark_positioning : 1;
-  const struct hb_ot_complex_shaper_t *shaper;
+  const struct hb_ot_shaper_t *shaper;
 
   HB_INTERNAL hb_ot_shape_planner_t (hb_face_t                     *face,
 				     const hb_segment_properties_t *props);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-fallback.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-fallback.hh
similarity index 82%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-fallback.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-fallback.hh
index 78f46c1cac1592f2ad81c8e2aae8b119954df8bb..b9f92f72d67c30dc918b85b3917aa4b4c420d518 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-fallback.hh
@@ -24,8 +24,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
-#define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
+#ifndef HB_OT_SHAPER_ARABIC_FALLBACK_HH
+#define HB_OT_SHAPER_ARABIC_FALLBACK_HH
 
 #include "hb.hh"
 
@@ -34,7 +34,11 @@
 
 
 /* Features ordered the same as the entries in shaping_table rows,
- * followed by rlig.  Don't change. */
+ * followed by rlig.  Don't change.
+ *
+ * We currently support one subtable per lookup, and one lookup
+ * per feature.  But we allow duplicate features, so we use that!
+ */
 static const hb_tag_t arabic_fallback_features[] =
 {
   HB_TAG('i','n','i','t'),
@@ -42,6 +46,8 @@ static const hb_tag_t arabic_fallback_features[] =
   HB_TAG('f','i','n','a'),
   HB_TAG('i','s','o','l'),
   HB_TAG('r','l','i','g'),
+  HB_TAG('r','l','i','g'),
+  HB_TAG('r','l','i','g'),
 };
 
 static OT::SubstLookup *
@@ -95,20 +101,25 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS
   return ret && !c.in_error () ? c.copy<OT::SubstLookup> () : nullptr;
 }
 
+template <typename T>
 static OT::SubstLookup *
 arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED,
-					    hb_font_t *font)
+					    hb_font_t *font,
+					    const T &ligature_table,
+					    unsigned lookup_flags)
 {
   OT::HBGlyphID16 first_glyphs[ARRAY_LENGTH_CONST (ligature_table)];
   unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)];
   unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)];
   unsigned int num_first_glyphs = 0;
 
-  /* We know that all our ligatures are 2-component */
+  /* We know that all our ligatures have the same number of components. */
   OT::HBGlyphID16 ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)];
   unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)];
-  OT::HBGlyphID16 component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */];
+  OT::HBGlyphID16 component_list[ARRAY_LENGTH_CONST (ligature_list) *
+				 ARRAY_LENGTH_CONST (ligature_table[0].ligatures[0].components)];
   unsigned int num_ligatures = 0;
+  unsigned int num_components = 0;
 
   /* Populate arrays */
 
@@ -133,21 +144,32 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN
   {
     unsigned int first_glyph_idx = first_glyphs_indirection[i];
 
-    for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++)
+    for (unsigned int ligature_idx = 0; ligature_idx < ARRAY_LENGTH (ligature_table[0].ligatures); ligature_idx++)
     {
-      hb_codepoint_t second_u   = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second;
-      hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature;
-      hb_codepoint_t second_glyph, ligature_glyph;
-      if (!second_u ||
-	  !hb_font_get_glyph (font, second_u,   0, &second_glyph) ||
-	  !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph))
+      hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[ligature_idx].ligature;
+      hb_codepoint_t ligature_glyph;
+      if (!hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph))
 	continue;
 
-      ligature_per_first_glyph_count_list[i]++;
+      const auto &components = ligature_table[first_glyph_idx].ligatures[ligature_idx].components;
+      unsigned component_count = ARRAY_LENGTH_CONST (components);
+
+      for (unsigned i = 0; i < component_count; i++)
+      {
+	hb_codepoint_t component_u   = ligature_table[first_glyph_idx].ligatures[ligature_idx].components[i];
+	hb_codepoint_t component_glyph;
+	if (!component_u ||
+	    !hb_font_get_glyph (font, component_u,   0, &component_glyph))
+	  continue;
 
+	component_list[num_components++] = component_glyph;
+      }
+
+      component_count_list[num_ligatures] = 1 + component_count;
       ligature_list[num_ligatures] = ligature_glyph;
-      component_count_list[num_ligatures] = 2;
-      component_list[num_ligatures] = second_glyph;
+
+      ligature_per_first_glyph_count_list[i]++;
+
       num_ligatures++;
     }
   }
@@ -161,14 +183,13 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN
   hb_serialize_context_t c (buf, sizeof (buf));
   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
   bool ret = lookup->serialize_ligature (&c,
-					 OT::LookupFlag::IgnoreMarks,
+					 lookup_flags,
 					 hb_sorted_array (first_glyphs, num_first_glyphs),
 					 hb_array (ligature_per_first_glyph_count_list, num_first_glyphs),
 					 hb_array (ligature_list, num_ligatures),
 					 hb_array (component_count_list, num_ligatures),
-					 hb_array (component_list, num_ligatures));
+					 hb_array (component_list, num_components));
   c.end_serialize ();
-  /* TODO sanitize the results? */
 
   return ret && !c.in_error () ? c.copy<OT::SubstLookup> () : nullptr;
 }
@@ -181,10 +202,18 @@ arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan,
   if (feature_index < 4)
     return arabic_fallback_synthesize_lookup_single (plan, font, feature_index);
   else
-    return arabic_fallback_synthesize_lookup_ligature (plan, font);
+  {
+    switch (feature_index) {
+      case 4: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_3_table, OT::LookupFlag::IgnoreMarks);
+      case 5: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_table, OT::LookupFlag::IgnoreMarks);
+      case 6: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_mark_table, 0);
+    }
+  }
+  assert (false);
+  return nullptr;
 }
 
-#define ARABIC_FALLBACK_MAX_LOOKUPS 5
+#define ARABIC_FALLBACK_MAX_LOOKUPS ARRAY_LENGTH_CONST (arabic_fallback_features)
 
 struct arabic_fallback_plan_t
 {
@@ -201,7 +230,7 @@ struct arabic_fallback_plan_t
 #endif
 
 #ifdef HB_WITH_WIN1256
-#include "hb-ot-shape-complex-arabic-win1256.hh"
+#include "hb-ot-shaper-arabic-win1256.hh"
 #endif
 
 struct ManifestLookup
@@ -230,9 +259,8 @@ arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan HB_UNUS
     return false;
 
   const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest);
-  static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) ==
+  static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) <=
 		 ARABIC_FALLBACK_MAX_LOOKUPS * sizeof (ManifestLookup), "");
-  /* TODO sanitize the table? */
 
   unsigned j = 0;
   unsigned int count = manifest.len;
@@ -264,7 +292,7 @@ arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan,
 				   const hb_ot_shape_plan_t *plan,
 				   hb_font_t *font)
 {
-  static_assert ((ARRAY_LENGTH_CONST(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS), "");
+  static_assert ((ARRAY_LENGTH_CONST (arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS), "");
   unsigned int j = 0;
   for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++)
   {
@@ -345,4 +373,4 @@ arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan,
 }
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */
+#endif /* HB_OT_SHAPER_ARABIC_FALLBACK_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-joining-list.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-joining-list.hh
similarity index 85%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-joining-list.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-joining-list.hh
index e6339ee72bc8dfb6e00cdd3761ffa840fb755237..a5ce3a6c93beaa2f92dd5c218ad516171e4ec224 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-joining-list.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-joining-list.hh
@@ -12,8 +12,8 @@
  * # Date: 2021-07-10, 00:35:31 GMT
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH
-#define HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH
+#ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH
+#define HB_OT_SHAPER_ARABIC_JOINING_LIST_HH
 
 static bool
 has_arabic_joining (hb_script_t script)
@@ -42,6 +42,6 @@ has_arabic_joining (hb_script_t script)
 }
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH */
+#endif /* HB_OT_SHAPER_ARABIC_JOINING_LIST_HH */
 
 /* == End of generated function == */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-pua.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-pua.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ba86772f849a833a3a9c595b34c0cc82f8b5d400
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-pua.hh
@@ -0,0 +1,118 @@
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ *   ./gen-arabic-pua.py
+ *
+ */
+
+#ifndef HB_OT_SHAPER_ARABIC_PUA_HH
+#define HB_OT_SHAPER_ARABIC_PUA_HH
+
+static const uint8_t
+_hb_arabic_u8[464] =
+{
+   84, 86, 85, 85, 85, 85, 85,213, 16, 34, 34, 34, 34, 34, 35, 34,
+   34, 34, 34, 34, 34, 34, 34, 34, 36, 34, 34, 34, 34, 34, 34, 34,
+   34, 34, 34, 34, 34, 34, 82, 16,  0,  0,  0,  0,  1,  2,  3,  4,
+    0,  0,  0,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0,  7,
+    0,  0,  8,  0,  0,  0,  9,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0, 11, 12, 13, 14, 15,
+   16, 17, 18, 19, 20, 21,  0,  0,  0, 22,  0, 23,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0, 24, 25, 26, 27, 28, 29, 30, 31,
+   32, 33, 34, 35, 36, 37, 38, 39, 16, 34, 34, 34, 35, 34, 34, 34,
+   34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+   34, 34, 34, 34, 34, 34, 34, 66, 16, 50, 68, 68, 68, 68, 68, 68,
+   68, 68, 68, 68,101, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
+   71, 68, 68, 68, 68, 68, 68, 68,152,186, 76, 77, 68,254, 16, 50,
+    0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  3,  4,  0,  0,  5,  6,
+    0,  0,  0,  0,  0,  0,  7,  8,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  9,  0,  0,  0, 10,  0,
+    0,  0,  0,  0,  0, 11,  0,  0,  0,  0,  0,  0,  0, 12,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0, 13,  0,  0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+   24, 25, 26, 27, 28, 23, 23, 29, 30, 31, 32, 33,  0,  0,  0,  0,
+    0,  0,  0, 34,  0,  0,  0, 35,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0, 36, 37, 38,  0,  0,  0,  0,  0,  0,  0, 39,  0,  0, 40,
+   41, 42,  0, 43, 44,  0,  0, 45, 46,  0, 47, 48, 49,  0,  0,  0,
+    0, 50,  0,  0, 51, 52,  0, 53, 54, 55, 56, 57, 58,  0,  0,  0,
+    0,  0, 59, 60, 61, 62, 63, 64,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 65,  0,  0, 66,
+    0,  0, 67,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+   84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+};
+static const uint16_t
+_hb_arabic_u16[720] =
+{
+      0,    0,    0,    0,    0,    0,    0,    0,61728,61729,61730,    0,    0,61733,    0,    0,
+  61736,61737,61738,61739,61790,61741,61742,61743,61872,61873,61874,61875,61876,61877,61878,61879,
+  61880,61881,61754,61755,    0,61757,    0,61759,    0,    0,    0,61787,61788,61789,    0,    0,
+      0,    0,    0,61731,    0,    0,    0,    0,    0,    0,    0,61732,    0,    0,    0,    0,
+      0,    0,    0,    0,    0,    0,    0,61734,    0,    0,    0,    0,    0,    0,    0,61735,
+      0,    0,    0,    0,61740,    0,    0,    0,    0,    0,    0,61755,    0,    0,    0,61759,
+      0,61869,61765,61763,61883,61767,61882,61761,61770,61865,61772,61774,61777,61780,61783,61784,
+  61785,61786,61792,61794,61796,61798,61800,61801,61802,61806,61810,61696,61696,61696,61696,61696,
+  61791,61813,61816,61818,61820,61822,61921,61860,61861,61868,61864,61895,61896,61899,61892,61893,
+  61898,61897,61894,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696,    0,
+  61744,61745,61746,61747,61748,61749,61750,61751,61752,61753,    0,61790,61790,    0,    0,    0,
+      0,    0,    0,    0,61708,61709,61710,61711,61756,61758,    0,    0,    0,    0,    0,    0,
+      0,61765,61766,61763,61764,61883,61883,61767,61768,61882,61871,61870,61870,61761,61762,61770,
+  61770,61769,61769,61865,61866,61772,61772,61771,61771,61774,61774,61773,61773,61777,61776,61775,
+  61775,61780,61779,61778,61778,61783,61782,61781,61781,61784,61784,61785,61785,61786,61786,61792,
+  61792,61794,61794,61793,61793,61796,61796,61795,61795,61798,61798,61797,61797,61800,61800,61799,
+  61799,61801,61801,61801,61801,61802,61802,61802,61802,61806,61805,61803,61804,61810,61809,61807,
+  61808,61813,61813,61811,61812,61816,61816,61814,61815,61818,61818,61817,61817,61820,61820,61819,
+  61819,61822,61822,61821,61821,61921,61921,61823,61823,61860,61859,61857,61858,61861,61861,61868,
+  61867,61864,61863,61862,61862,61888,61889,61886,61887,61890,61891,61885,61884,    0,    0,    0,
+      0,    0,    0,    0,61984,61985,61986,    0,    0,61989,    0,    0,61992,61993,61994,61995,
+  62046,61997,61998,61999,    0,    0,62010,62011,    0,62013,    0,62015,    0,    0,    0,62043,
+      0,62045,    0,    0,    0,    0,    0,61987,    0,    0,    0,61988,    0,    0,    0,61990,
+      0,    0,    0,61991,61996,    0,    0,    0,    0,    0,    0,62011,    0,    0,    0,62015,
+      0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,62053,
+  62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,61952,61952,61952,61952,
+  62047,62130,62134,62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181,
+  62186,62185,62182,61952,61952,61952,61952,    0,62000,62001,62002,62003,62004,62005,62006,62007,
+  62008,62009,    0,62046,62046,    0,    0,    0,61964,61965,61966,61967,62012,62014,    0,    0,
+  61954,    0,61981,    0,    0,    0,61955,    0,61982,    0,61956,    0,    0,    0,62111,    0,
+      0,    0,    0,61970,61971,61972,61957,    0,61980,    0,    0,    0,    0,    0,61958,    0,
+  61983,    0,    0,    0,    0,    0,62191,    0,62188,62189,62192,    0,    0,    0,61973,    0,
+      0,62098,    0,    0,61974,    0,    0,62099,    0,    0,62101,    0,    0,61975,    0,    0,
+  62100,    0,    0,    0,62080,62081,62082,62102,    0,62083,62084,62085,62103,    0,    0,    0,
+  62106,    0,62107,    0,62108,    0,    0,    0,61976,    0,    0,    0,    0,62086,62087,62088,
+  62109,61978,62089,62090,62091,62110,62093,62094,    0,62104,    0,    0,    0,    0,62095,62096,
+  62097,62105,    0,    0,61977,    0,    0,    0,    0,    0,62075,62077,61968,    0,    0,    0,
+      0,62021,62022,62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028,
+  62027,62025,62026,62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037,
+  62038,62048,62044,62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059,
+  62060,62064,62063,62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073,
+  62074,62114,62113,62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123,
+  62124,62130,62129,62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139,
+  62140,62146,62145,62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164,
+  62163,62160,62159,62157,62158,62176,62177,62174,62175,62178,62179,62172,62173,    0,    0,    0,
+};
+
+static inline unsigned
+_hb_arabic_b2 (const uint8_t* a, unsigned i)
+{
+  return (a[i>>2]>>((i&3u)<<1))&3u;
+}
+static inline unsigned
+_hb_arabic_b4 (const uint8_t* a, unsigned i)
+{
+  return (a[i>>1]>>((i&1u)<<2))&15u;
+}
+static inline uint_fast16_t
+_hb_arabic_pua_simp_map (unsigned u)
+{
+  return u<65277u?_hb_arabic_u16[((_hb_arabic_u8[40+(((_hb_arabic_b4(8+_hb_arabic_u8,((_hb_arabic_b2(_hb_arabic_u8,u>>3>>4>>4))<<4)+((u>>3>>4)&15u)))<<4)+((u>>3)&15u))])<<3)+((u)&7u)]:0;
+}
+static inline uint_fast16_t
+_hb_arabic_pua_trad_map (unsigned u)
+{
+  return u<65277u?_hb_arabic_u16[320+(((_hb_arabic_u8[208+(((_hb_arabic_b4(168+_hb_arabic_u8,((_hb_arabic_b4(136+_hb_arabic_u8,u>>2>>4>>4))<<4)+((u>>2>>4)&15u)))<<4)+((u>>2)&15u))])<<2)+((u)&3u))]:0;
+}
+
+#endif /* HB_OT_SHAPER_ARABIC_PUA_HH */
+
+/* == End of generated table == */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-table.hh
similarity index 75%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-table.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-table.hh
index c158964f2cd7cc9825eecf51e90d92b750e717e1..fd3d8645d1daacd9e25bf210f8302741334ff174 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-table.hh
@@ -13,8 +13,8 @@
  * UnicodeData.txt does not have a header.
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
-#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
+#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH
+#define HB_OT_SHAPER_ARABIC_TABLE_HH
 
 
 #define A	JOINING_GROUP_ALAPH
@@ -416,26 +416,141 @@ static const uint16_t shaping_table[][4] =
 static const struct ligature_set_t {
  uint16_t first;
  struct ligature_pairs_t {
-   uint16_t second;
+   uint16_t components[1];
    uint16_t ligature;
- } ligatures[4];
+ } ligatures[14];
 } ligature_table[] =
 {
+  { 0xFE91u, {
+    { {0xFEE2u}, 0xFC08u }, /* ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM */
+    { {0xFEE4u}, 0xFC9Fu }, /* ARABIC LIGATURE BEH WITH MEEM INITIAL FORM */
+    { {0xFEA0u}, 0xFC9Cu }, /* ARABIC LIGATURE BEH WITH JEEM INITIAL FORM */
+    { {0xFEA4u}, 0xFC9Du }, /* ARABIC LIGATURE BEH WITH HAH INITIAL FORM */
+    { {0xFEA8u}, 0xFC9Eu }, /* ARABIC LIGATURE BEH WITH KHAH INITIAL FORM */
+  }},
+  { 0xFE92u, {
+    { {0xFEAEu}, 0xFC6Au }, /* ARABIC LIGATURE BEH WITH REH FINAL FORM */
+    { {0xFEE6u}, 0xFC6Du }, /* ARABIC LIGATURE BEH WITH NOON FINAL FORM */
+    { {0xFEF2u}, 0xFC6Fu }, /* ARABIC LIGATURE BEH WITH YEH FINAL FORM */
+  }},
+  { 0xFE97u, {
+    { {0xFEE2u}, 0xFC0Eu }, /* ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM */
+    { {0xFEE4u}, 0xFCA4u }, /* ARABIC LIGATURE TEH WITH MEEM INITIAL FORM */
+    { {0xFEA0u}, 0xFCA1u }, /* ARABIC LIGATURE TEH WITH JEEM INITIAL FORM */
+    { {0xFEA4u}, 0xFCA2u }, /* ARABIC LIGATURE TEH WITH HAH INITIAL FORM */
+    { {0xFEA8u}, 0xFCA3u }, /* ARABIC LIGATURE TEH WITH KHAH INITIAL FORM */
+  }},
+  { 0xFE98u, {
+    { {0xFEAEu}, 0xFC70u }, /* ARABIC LIGATURE TEH WITH REH FINAL FORM */
+    { {0xFEE6u}, 0xFC73u }, /* ARABIC LIGATURE TEH WITH NOON FINAL FORM */
+    { {0xFEF2u}, 0xFC75u }, /* ARABIC LIGATURE TEH WITH YEH FINAL FORM */
+  }},
+  { 0xFE9Bu, {
+    { {0xFEE2u}, 0xFC12u }, /* ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM */
+  }},
+  { 0xFE9Fu, {
+    { {0xFEE4u}, 0xFCA8u }, /* ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM */
+  }},
+  { 0xFEA3u, {
+    { {0xFEE4u}, 0xFCAAu }, /* ARABIC LIGATURE HAH WITH MEEM INITIAL FORM */
+  }},
+  { 0xFEA7u, {
+    { {0xFEE4u}, 0xFCACu }, /* ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM */
+  }},
+  { 0xFEB3u, {
+    { {0xFEE4u}, 0xFCB0u }, /* ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM */
+  }},
+  { 0xFEB7u, {
+    { {0xFEE4u}, 0xFD30u }, /* ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM */
+  }},
+  { 0xFED3u, {
+    { {0xFEF2u}, 0xFC32u }, /* ARABIC LIGATURE FEH WITH YEH ISOLATED FORM */
+  }},
   { 0xFEDFu, {
-    { 0xFE82u, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */
-    { 0xFE84u, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */
-    { 0xFE88u, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */
-    { 0xFE8Eu, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */
+    { {0xFE9Eu}, 0xFC3Fu }, /* ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM */
+    { {0xFEA0u}, 0xFCC9u }, /* ARABIC LIGATURE LAM WITH JEEM INITIAL FORM */
+    { {0xFEA2u}, 0xFC40u }, /* ARABIC LIGATURE LAM WITH HAH ISOLATED FORM */
+    { {0xFEA4u}, 0xFCCAu }, /* ARABIC LIGATURE LAM WITH HAH INITIAL FORM */
+    { {0xFEA6u}, 0xFC41u }, /* ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM */
+    { {0xFEA8u}, 0xFCCBu }, /* ARABIC LIGATURE LAM WITH KHAH INITIAL FORM */
+    { {0xFEE2u}, 0xFC42u }, /* ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM */
+    { {0xFEE4u}, 0xFCCCu }, /* ARABIC LIGATURE LAM WITH MEEM INITIAL FORM */
+    { {0xFEF2u}, 0xFC44u }, /* ARABIC LIGATURE LAM WITH YEH ISOLATED FORM */
+    { {0xFEECu}, 0xFCCDu }, /* ARABIC LIGATURE LAM WITH HEH INITIAL FORM */
+    { {0xFE82u}, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */
+    { {0xFE84u}, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */
+    { {0xFE88u}, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */
+    { {0xFE8Eu}, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */
   }},
   { 0xFEE0u, {
-    { 0xFE82u, 0xFEF6u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */
-    { 0xFE84u, 0xFEF8u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */
-    { 0xFE88u, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */
-    { 0xFE8Eu, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */
+    { {0xFEF0u}, 0xFC86u }, /* ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM */
+    { {0xFE82u}, 0xFEF6u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */
+    { {0xFE84u}, 0xFEF8u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */
+    { {0xFE88u}, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */
+    { {0xFE8Eu}, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */
+  }},
+  { 0xFEE3u, {
+    { {0xFEA0u}, 0xFCCEu }, /* ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM */
+    { {0xFEA4u}, 0xFCCFu }, /* ARABIC LIGATURE MEEM WITH HAH INITIAL FORM */
+    { {0xFEA8u}, 0xFCD0u }, /* ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM */
+    { {0xFEE4u}, 0xFCD1u }, /* ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM */
+  }},
+  { 0xFEE7u, {
+    { {0xFEE2u}, 0xFC4Eu }, /* ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM */
+    { {0xFEE4u}, 0xFCD5u }, /* ARABIC LIGATURE NOON WITH MEEM INITIAL FORM */
+    { {0xFEA0u}, 0xFCD2u }, /* ARABIC LIGATURE NOON WITH JEEM INITIAL FORM */
+    { {0xFEA4u}, 0xFCD3u }, /* ARABIC LIGATURE NOON WITH HAH INITIAL FORM */
+  }},
+  { 0xFEE8u, {
+    { {0xFEF2u}, 0xFC8Fu }, /* ARABIC LIGATURE NOON WITH YEH FINAL FORM */
+  }},
+  { 0xFEF3u, {
+    { {0xFEA0u}, 0xFCDAu }, /* ARABIC LIGATURE YEH WITH JEEM INITIAL FORM */
+    { {0xFEA4u}, 0xFCDBu }, /* ARABIC LIGATURE YEH WITH HAH INITIAL FORM */
+    { {0xFEA8u}, 0xFCDCu }, /* ARABIC LIGATURE YEH WITH KHAH INITIAL FORM */
+    { {0xFEE4u}, 0xFCDDu }, /* ARABIC LIGATURE YEH WITH MEEM INITIAL FORM */
+  }},
+  { 0xFEF4u, {
+    { {0xFEAEu}, 0xFC91u }, /* ARABIC LIGATURE YEH WITH REH FINAL FORM */
+    { {0xFEE6u}, 0xFC94u }, /* ARABIC LIGATURE YEH WITH NOON FINAL FORM */
+  }},
+};
+
+
+static const struct ligature_mark_set_t {
+ uint16_t first;
+ struct ligature_pairs_t {
+   uint16_t components[1];
+   uint16_t ligature;
+ } ligatures[5];
+} ligature_mark_table[] =
+{
+  { 0x0651u, {
+    { {0x064Cu}, 0xFC5Eu }, /* ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM */
+    { {0x064Eu}, 0xFC60u }, /* ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM */
+    { {0x064Fu}, 0xFC61u }, /* ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM */
+    { {0x0650u}, 0xFC62u }, /* ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM */
+    { {0x064Bu}, 0xF2EEu }, /* PUA ARABIC LIGATURE SHADDA WITH FATHATAN ISOLATED FORM */
+  }},
+};
+
+
+static const struct ligature_3_set_t {
+ uint16_t first;
+ struct ligature_triplets_t {
+   uint16_t components[2];
+   uint16_t ligature;
+ } ligatures[3];
+} ligature_3_table[] =
+{
+  { 0xFEDFu, {
+    { {0xFEE4u, 0xFEA4u}, 0xFD88u}, /* ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM */
+    { {0xFEE0u, 0xFEEAu}, 0xF201u}, /* PUA ARABIC LIGATURE LELLAH ISOLATED FORM */
+    { {0xFEE4u, 0xFEA0u}, 0xF211u}, /* PUA ARABIC LIGATURE LAM WITH MEEM WITH JEEM INITIAL FORM */
   }},
 };
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */
+#endif /* HB_OT_SHAPER_ARABIC_TABLE_HH */
 
 /* == End of generated table == */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-win1256.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-win1256.hh
similarity index 98%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-win1256.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-win1256.hh
index 429974d05b48880d8a003e411d6d09dee9920b44..b8d481c8134a940f096b1d196253fd64c623e527 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic-win1256.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-win1256.hh
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH
+#ifndef HB_OT_SHAPER_ARABIC_WIN1256_HH
 
 
 /*
@@ -342,8 +342,8 @@ OT_TABLE_END
 #include "hb.hh" /* Make check-includes.sh happy. */
 #endif
 #ifdef OT_MEASURE
-#include "hb-ot-shape-complex-arabic-win1256.hh"
+#include "hb-ot-shaper-arabic-win1256.hh"
 #endif
 
-#define HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH
-#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH */
+#define HB_OT_SHAPER_ARABIC_WIN1256_HH
+#endif /* HB_OT_SHAPER_ARABIC_WIN1256_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic.cc
similarity index 97%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic.cc
index 224f8b842eb9063ef146291b47e63fd049b098f3..e869d78509a4a84fc76ce9c10553a6a1d3cfe93a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic.cc
@@ -28,14 +28,14 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex-arabic.hh"
+#include "hb-ot-shaper-arabic.hh"
 #include "hb-ot-shape.hh"
 
 
 /* buffer var allocations */
-#define arabic_shaping_action() complex_var_u8_auxiliary() /* arabic shaping action */
+#define arabic_shaping_action() ot_shaper_var_u8_auxiliary() /* arabic shaping action */
 
-#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0
+#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_SHAPER0
 
 /* See:
  * https://github.com/harfbuzz/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */
@@ -81,7 +81,7 @@ enum hb_arabic_joining_type_t {
   JOINING_TYPE_X = 8  /* means: use general-category to choose between U or T. */
 };
 
-#include "hb-ot-shape-complex-arabic-table.hh"
+#include "hb-ot-shaper-arabic-table.hh"
 
 static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
 {
@@ -171,6 +171,14 @@ record_stch (const hb_ot_shape_plan_t *plan,
 	     hb_font_t *font,
 	     hb_buffer_t *buffer);
 
+static void
+deallocate_buffer_var (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
+{
+  HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
+}
+
 static void
 collect_features_arabic (hb_ot_shape_planner_t *plan)
 {
@@ -213,6 +221,7 @@ collect_features_arabic (hb_ot_shape_planner_t *plan)
     map->add_feature (arabic_features[i], has_fallback ? F_HAS_FALLBACK : F_NONE);
     map->add_gsub_pause (nullptr);
   }
+   map->add_gsub_pause (deallocate_buffer_var);
 
   /* Normally, Unicode says a ZWNJ means "don't ligate".  In Arabic script
    * however, it says a ZWJ should also mean "don't ligate".  So we run
@@ -240,7 +249,7 @@ collect_features_arabic (hb_ot_shape_planner_t *plan)
   map->enable_feature (HB_TAG('m','s','e','t'));
 }
 
-#include "hb-ot-shape-complex-arabic-fallback.hh"
+#include "hb-ot-shaper-arabic-fallback.hh"
 
 struct arabic_shape_plan_t
 {
@@ -405,7 +414,7 @@ arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
-#ifdef HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK
+#ifdef HB_NO_OT_SHAPER_ARABIC_FALLBACK
   return;
 #endif
 
@@ -619,8 +628,6 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan,
 			   hb_font_t                *font)
 {
   apply_stch (plan, buffer, font);
-
-  HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
 /* https://www.unicode.org/reports/tr53/ */
@@ -689,7 +696,7 @@ reorder_marks_arabic (const hb_ot_shape_plan_t *plan HB_UNUSED,
 
     /* Shift it! */
     DEBUG_MSG (ARABIC, buffer, "Shifting %d's: %d %d", cc, i, j);
-    hb_glyph_info_t temp[HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS];
+    hb_glyph_info_t temp[HB_OT_SHAPE_MAX_COMBINING_MARKS];
     assert (j - i <= ARRAY_LENGTH (temp));
     buffer->merge_clusters (start, j);
     memmove (temp, &info[i], (j - i) * sizeof (hb_glyph_info_t));
@@ -720,7 +727,7 @@ reorder_marks_arabic (const hb_ot_shape_plan_t *plan HB_UNUSED,
   }
 }
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
+const hb_ot_shaper_t _hb_ot_shaper_arabic =
 {
   collect_features_arabic,
   nullptr, /* override_features */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic.hh
similarity index 90%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic.hh
index 5bf6ff6338a1a2a99d933c7c06009c0547b55e3c..a025b1a399ceecc59110b899df6e26f2ffe9a856 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-arabic.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic.hh
@@ -26,12 +26,12 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_HH
-#define HB_OT_SHAPE_COMPLEX_ARABIC_HH
+#ifndef HB_OT_SHAPER_ARABIC_HH
+#define HB_OT_SHAPER_ARABIC_HH
 
 #include "hb.hh"
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 
 
 struct arabic_shape_plan_t;
@@ -47,4 +47,4 @@ setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan,
 			 hb_buffer_t               *buffer,
 			 hb_script_t                script);
 
-#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_HH */
+#endif /* HB_OT_SHAPER_ARABIC_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-default.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-default.cc
similarity index 93%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-default.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-default.cc
index a755aea098b6ca137eaf805e72425d11ece16b4b..25716aa81f525c4be61231e5ab91c3a7b5672547 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-default.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-default.cc
@@ -28,10 +28,10 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
+const hb_ot_shaper_t _hb_ot_shaper_default =
 {
   nullptr, /* collect_features */
   nullptr, /* override_features */
@@ -51,7 +51,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
 
 /* Same as default but no mark advance zeroing / fallback positioning.
  * Dumbest shaper ever, basically. */
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_dumber =
+const hb_ot_shaper_t _hb_ot_shaper_dumber =
 {
   nullptr, /* collect_features */
   nullptr, /* override_features */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-hangul.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-hangul.cc
similarity index 98%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-hangul.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-hangul.cc
index 3bc9e9b961cd54c434dcdfb0d40e650a28f62cc3..aa507c75caac57c1204e62ca32b9c82409356033 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-hangul.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-hangul.cc
@@ -28,7 +28,7 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 
 
 /* Hangul shaper */
@@ -119,7 +119,7 @@ data_destroy_hangul (void *data)
 #define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302Eu, 0x302Fu))
 
 /* buffer var allocations */
-#define hangul_shaping_feature() complex_var_u8_auxiliary() /* hangul jamo shaping feature */
+#define hangul_shaping_feature() ot_shaper_var_u8_auxiliary() /* hangul jamo shaping feature */
 
 static bool
 is_zero_width_char (hb_font_t *font,
@@ -414,7 +414,7 @@ setup_masks_hangul (const hb_ot_shape_plan_t *plan,
 }
 
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul =
+const hb_ot_shaper_t _hb_ot_shaper_hangul =
 {
   collect_features_hangul,
   override_features_hangul,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-hebrew.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-hebrew.cc
similarity index 97%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-hebrew.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-hebrew.cc
index 334d3ded82f7a9d392597334207f83caf6edcb45..f3b6cde17969a8fa99b1ebb6875dacf1db877200 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-hebrew.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-hebrew.cc
@@ -28,7 +28,7 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 
 
 static bool
@@ -74,7 +74,7 @@ compose_hebrew (const hb_ot_shape_normalize_context_t *c,
 
   bool found = (bool) c->unicode->compose (a, b, ab);
 
-#ifdef HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK
+#ifdef HB_NO_OT_SHAPER_HEBREW_FALLBACK
   return found;
 #endif
 
@@ -163,7 +163,7 @@ compose_hebrew (const hb_ot_shape_normalize_context_t *c,
 }
 
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
+const hb_ot_shaper_t _hb_ot_shaper_hebrew =
 {
   nullptr, /* collect_features */
   nullptr, /* override_features */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-machine.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d52b13f6161d48fb7864c74211b8f5fc0b3dadaa
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-machine.hh
@@ -0,0 +1,589 @@
+
+#line 1 "hb-ot-shaper-indic-machine.rl"
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPER_INDIC_MACHINE_HH
+#define HB_OT_SHAPER_INDIC_MACHINE_HH
+
+#include "hb.hh"
+
+#include "hb-ot-layout.hh"
+#include "hb-ot-shaper-indic.hh"
+
+/* buffer var allocations */
+#define indic_category() ot_shaper_var_u8_category() /* indic_category_t */
+#define indic_position() ot_shaper_var_u8_auxiliary() /* indic_position_t */
+
+using indic_category_t = unsigned;
+using indic_position_t = ot_position_t;
+
+#define I_Cat(Cat) indic_syllable_machine_ex_##Cat
+
+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 57 "hb-ot-shaper-indic-machine.hh"
+#define indic_syllable_machine_ex_A 9u
+#define indic_syllable_machine_ex_C 1u
+#define indic_syllable_machine_ex_CM 16u
+#define indic_syllable_machine_ex_CS 18u
+#define indic_syllable_machine_ex_DOTTEDCIRCLE 11u
+#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 10u
+#define indic_syllable_machine_ex_RS 12u
+#define indic_syllable_machine_ex_Ra 15u
+#define indic_syllable_machine_ex_Repha 14u
+#define indic_syllable_machine_ex_SM 8u
+#define indic_syllable_machine_ex_Symbol 17u
+#define indic_syllable_machine_ex_V 2u
+#define indic_syllable_machine_ex_VD 9u
+#define indic_syllable_machine_ex_X 0u
+#define indic_syllable_machine_ex_ZWJ 6u
+#define indic_syllable_machine_ex_ZWNJ 5u
+
+
+#line 79 "hb-ot-shaper-indic-machine.hh"
+static const unsigned char _indic_syllable_machine_trans_keys[] = {
+	8u, 8u, 4u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 4u, 12u, 4u, 8u, 8u, 8u, 
+	5u, 7u, 5u, 8u, 4u, 8u, 4u, 12u, 4u, 12u, 4u, 12u, 8u, 8u, 5u, 7u, 
+	5u, 8u, 4u, 8u, 4u, 8u, 4u, 12u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 
+	4u, 8u, 5u, 8u, 8u, 8u, 1u, 18u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 
+	5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, 1u, 15u, 1u, 15u, 3u, 9u, 
+	4u, 9u, 5u, 9u, 4u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 
+	3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 
+	5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, 
+	4u, 9u, 5u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 4u, 8u, 
+	3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 
+	5u, 9u, 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, 3u, 16u, 4u, 9u, 
+	5u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 4u, 12u, 4u, 8u, 3u, 16u, 
+	3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, 
+	1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, 3u, 16u, 4u, 9u, 5u, 9u, 
+	5u, 9u, 3u, 9u, 5u, 9u, 1u, 16u, 3u, 16u, 1u, 16u, 4u, 12u, 5u, 9u, 
+	9u, 9u, 5u, 9u, 1u, 15u, 3u, 9u, 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, 
+	1u, 15u, 0
+};
+
+static const char _indic_syllable_machine_key_spans[] = {
+	1, 5, 3, 4, 5, 9, 5, 1, 
+	3, 4, 5, 9, 9, 9, 1, 3, 
+	4, 5, 5, 9, 1, 3, 4, 5, 
+	5, 4, 1, 18, 14, 14, 13, 15, 
+	5, 5, 1, 5, 15, 15, 15, 7, 
+	6, 5, 6, 5, 7, 5, 14, 14, 
+	14, 14, 13, 15, 14, 14, 13, 15, 
+	5, 1, 5, 15, 15, 7, 6, 5, 
+	6, 5, 5, 7, 5, 14, 14, 5, 
+	14, 14, 13, 15, 14, 15, 5, 1, 
+	5, 15, 15, 7, 6, 5, 14, 6, 
+	5, 5, 7, 5, 14, 9, 5, 14, 
+	14, 13, 15, 14, 15, 5, 1, 5, 
+	15, 15, 7, 6, 5, 14, 6, 5, 
+	5, 7, 5, 16, 14, 16, 9, 5, 
+	1, 5, 15, 7, 5, 5, 1, 5, 
+	15
+};
+
+static const short _indic_syllable_machine_index_offsets[] = {
+	0, 2, 8, 12, 17, 23, 33, 39, 
+	41, 45, 50, 56, 66, 76, 86, 88, 
+	92, 97, 103, 109, 119, 121, 125, 130, 
+	136, 142, 147, 149, 168, 183, 198, 212, 
+	228, 234, 240, 242, 248, 264, 280, 296, 
+	304, 311, 317, 324, 330, 338, 344, 359, 
+	374, 389, 404, 418, 434, 449, 464, 478, 
+	494, 500, 502, 508, 524, 540, 548, 555, 
+	561, 568, 574, 580, 588, 594, 609, 624, 
+	630, 645, 660, 674, 690, 705, 721, 727, 
+	729, 735, 751, 767, 775, 782, 788, 803, 
+	810, 816, 822, 830, 836, 851, 861, 867, 
+	882, 897, 911, 927, 942, 958, 964, 966, 
+	972, 988, 1004, 1012, 1019, 1025, 1040, 1047, 
+	1053, 1059, 1067, 1073, 1090, 1105, 1122, 1132, 
+	1138, 1140, 1146, 1162, 1170, 1176, 1182, 1184, 
+	1190
+};
+
+static const unsigned char _indic_syllable_machine_indicies[] = {
+	1, 0, 2, 3, 3, 4, 1, 0, 
+	3, 3, 4, 0, 3, 3, 4, 1, 
+	0, 5, 3, 3, 4, 1, 0, 2, 
+	3, 3, 4, 1, 0, 0, 0, 6, 
+	0, 8, 9, 9, 10, 11, 7, 11, 
+	7, 9, 9, 10, 7, 9, 9, 10, 
+	11, 7, 12, 9, 9, 10, 11, 7, 
+	8, 9, 9, 10, 11, 7, 7, 7, 
+	13, 7, 8, 9, 9, 10, 11, 7, 
+	7, 7, 14, 7, 16, 17, 17, 18, 
+	19, 15, 15, 15, 20, 15, 19, 15, 
+	17, 17, 18, 21, 17, 17, 18, 19, 
+	15, 16, 17, 17, 18, 19, 15, 22, 
+	17, 17, 18, 19, 15, 24, 25, 25, 
+	26, 27, 23, 23, 23, 28, 23, 27, 
+	23, 25, 25, 26, 23, 25, 25, 26, 
+	27, 23, 24, 25, 25, 26, 27, 23, 
+	29, 25, 25, 26, 27, 23, 17, 17, 
+	18, 1, 0, 31, 30, 33, 34, 35, 
+	36, 37, 38, 18, 19, 39, 40, 40, 
+	20, 32, 41, 42, 43, 44, 45, 32, 
+	47, 48, 49, 50, 4, 1, 51, 46, 
+	46, 6, 46, 46, 46, 52, 46, 53, 
+	48, 54, 54, 4, 1, 51, 46, 46, 
+	46, 46, 46, 46, 52, 46, 48, 54, 
+	54, 4, 1, 51, 46, 46, 46, 46, 
+	46, 46, 52, 46, 33, 46, 46, 46, 
+	55, 56, 46, 1, 51, 46, 46, 46, 
+	46, 46, 33, 46, 57, 57, 46, 1, 
+	51, 46, 51, 46, 46, 58, 51, 46, 
+	51, 46, 51, 46, 46, 46, 51, 46, 
+	33, 46, 59, 46, 57, 57, 46, 1, 
+	51, 46, 46, 46, 46, 46, 33, 46, 
+	33, 46, 46, 46, 57, 57, 46, 1, 
+	51, 46, 46, 46, 46, 46, 33, 46, 
+	33, 46, 46, 46, 57, 56, 46, 1, 
+	51, 46, 46, 46, 46, 46, 33, 46, 
+	60, 61, 62, 62, 4, 1, 51, 46, 
+	61, 62, 62, 4, 1, 51, 46, 62, 
+	62, 4, 1, 51, 46, 63, 64, 64, 
+	4, 1, 51, 46, 55, 65, 46, 1, 
+	51, 46, 55, 46, 57, 57, 46, 1, 
+	51, 46, 57, 65, 46, 1, 51, 46, 
+	47, 48, 54, 54, 4, 1, 51, 46, 
+	46, 46, 46, 46, 46, 52, 46, 47, 
+	48, 49, 54, 4, 1, 51, 46, 46, 
+	6, 46, 46, 46, 52, 46, 67, 68, 
+	69, 70, 10, 11, 71, 66, 66, 14, 
+	66, 66, 66, 72, 66, 73, 68, 74, 
+	70, 10, 11, 71, 66, 66, 66, 66, 
+	66, 66, 72, 66, 68, 74, 70, 10, 
+	11, 71, 66, 66, 66, 66, 66, 66, 
+	72, 66, 75, 66, 66, 66, 76, 77, 
+	66, 11, 71, 66, 66, 66, 66, 66, 
+	75, 66, 78, 68, 79, 80, 10, 11, 
+	71, 66, 66, 13, 66, 66, 66, 72, 
+	66, 81, 68, 74, 74, 10, 11, 71, 
+	66, 66, 66, 66, 66, 66, 72, 66, 
+	68, 74, 74, 10, 11, 71, 66, 66, 
+	66, 66, 66, 66, 72, 66, 75, 66, 
+	66, 66, 82, 77, 66, 11, 71, 66, 
+	66, 66, 66, 66, 75, 66, 71, 66, 
+	66, 83, 71, 66, 71, 66, 71, 66, 
+	66, 66, 71, 66, 75, 66, 84, 66, 
+	82, 82, 66, 11, 71, 66, 66, 66, 
+	66, 66, 75, 66, 75, 66, 66, 66, 
+	82, 82, 66, 11, 71, 66, 66, 66, 
+	66, 66, 75, 66, 85, 86, 87, 87, 
+	10, 11, 71, 66, 86, 87, 87, 10, 
+	11, 71, 66, 87, 87, 10, 11, 71, 
+	66, 88, 89, 89, 10, 11, 71, 66, 
+	76, 90, 66, 11, 71, 66, 82, 82, 
+	66, 11, 71, 66, 76, 66, 82, 82, 
+	66, 11, 71, 66, 82, 90, 66, 11, 
+	71, 66, 78, 68, 74, 74, 10, 11, 
+	71, 66, 66, 66, 66, 66, 66, 72, 
+	66, 78, 68, 79, 74, 10, 11, 71, 
+	66, 66, 13, 66, 66, 66, 72, 66, 
+	8, 9, 9, 10, 11, 66, 67, 68, 
+	74, 70, 10, 11, 71, 66, 66, 66, 
+	66, 66, 66, 72, 66, 92, 36, 93, 
+	93, 18, 19, 39, 91, 91, 91, 91, 
+	91, 91, 43, 91, 36, 93, 93, 18, 
+	19, 39, 91, 91, 91, 91, 91, 91, 
+	43, 91, 94, 91, 91, 91, 95, 96, 
+	91, 19, 39, 91, 91, 91, 91, 91, 
+	94, 91, 35, 36, 97, 98, 18, 19, 
+	39, 91, 91, 20, 91, 91, 91, 43, 
+	91, 94, 91, 91, 91, 99, 96, 91, 
+	19, 39, 91, 91, 91, 91, 91, 94, 
+	91, 39, 91, 91, 100, 39, 91, 39, 
+	91, 39, 91, 91, 91, 39, 91, 94, 
+	91, 101, 91, 99, 99, 91, 19, 39, 
+	91, 91, 91, 91, 91, 94, 91, 94, 
+	91, 91, 91, 99, 99, 91, 19, 39, 
+	91, 91, 91, 91, 91, 94, 91, 102, 
+	103, 104, 104, 18, 19, 39, 91, 103, 
+	104, 104, 18, 19, 39, 91, 104, 104, 
+	18, 19, 39, 91, 35, 36, 93, 93, 
+	18, 19, 39, 91, 91, 91, 91, 91, 
+	91, 43, 91, 105, 106, 106, 18, 19, 
+	39, 91, 95, 107, 91, 19, 39, 91, 
+	99, 99, 91, 19, 39, 91, 95, 91, 
+	99, 99, 91, 19, 39, 91, 99, 107, 
+	91, 19, 39, 91, 35, 36, 97, 93, 
+	18, 19, 39, 91, 91, 20, 91, 91, 
+	91, 43, 91, 16, 17, 17, 18, 19, 
+	108, 108, 108, 20, 108, 16, 17, 17, 
+	18, 19, 108, 110, 111, 112, 113, 26, 
+	27, 114, 109, 109, 28, 109, 109, 109, 
+	115, 109, 116, 111, 113, 113, 26, 27, 
+	114, 109, 109, 109, 109, 109, 109, 115, 
+	109, 111, 113, 113, 26, 27, 114, 109, 
+	109, 109, 109, 109, 109, 115, 109, 117, 
+	109, 109, 109, 118, 119, 109, 27, 114, 
+	109, 109, 109, 109, 109, 117, 109, 110, 
+	111, 112, 40, 26, 27, 114, 109, 109, 
+	28, 109, 109, 109, 115, 109, 117, 109, 
+	109, 109, 120, 119, 109, 27, 114, 109, 
+	109, 109, 109, 109, 117, 109, 114, 109, 
+	109, 121, 114, 109, 114, 109, 114, 109, 
+	109, 109, 114, 109, 117, 109, 122, 109, 
+	120, 120, 109, 27, 114, 109, 109, 109, 
+	109, 109, 117, 109, 117, 109, 109, 109, 
+	120, 120, 109, 27, 114, 109, 109, 109, 
+	109, 109, 117, 109, 123, 124, 125, 125, 
+	26, 27, 114, 109, 124, 125, 125, 26, 
+	27, 114, 109, 125, 125, 26, 27, 114, 
+	109, 110, 111, 113, 113, 26, 27, 114, 
+	109, 109, 109, 109, 109, 109, 115, 109, 
+	126, 127, 127, 26, 27, 114, 109, 118, 
+	128, 109, 27, 114, 109, 120, 120, 109, 
+	27, 114, 109, 118, 109, 120, 120, 109, 
+	27, 114, 109, 120, 128, 109, 27, 114, 
+	109, 33, 34, 35, 36, 97, 93, 18, 
+	19, 39, 40, 40, 20, 91, 91, 33, 
+	43, 91, 47, 129, 49, 50, 4, 1, 
+	51, 46, 46, 6, 46, 46, 46, 52, 
+	46, 33, 34, 35, 36, 130, 131, 18, 
+	132, 133, 46, 40, 20, 46, 46, 33, 
+	43, 46, 16, 134, 134, 18, 132, 51, 
+	46, 46, 20, 46, 133, 46, 46, 135, 
+	133, 46, 133, 46, 133, 46, 46, 46, 
+	133, 46, 33, 46, 59, 16, 134, 134, 
+	18, 132, 51, 46, 46, 46, 46, 46, 
+	33, 46, 137, 136, 138, 138, 136, 31, 
+	139, 136, 138, 138, 136, 31, 139, 136, 
+	139, 136, 136, 140, 139, 136, 139, 136, 
+	139, 136, 136, 136, 139, 136, 33, 108, 
+	108, 108, 108, 108, 108, 108, 108, 40, 
+	108, 108, 108, 108, 33, 108, 0
+};
+
+static const unsigned char _indic_syllable_machine_trans_targs[] = {
+	27, 33, 38, 2, 39, 45, 46, 27, 
+	55, 8, 61, 56, 68, 69, 72, 27, 
+	77, 15, 83, 78, 86, 27, 91, 27, 
+	100, 21, 106, 101, 109, 114, 27, 125, 
+	27, 28, 48, 73, 75, 93, 94, 79, 
+	95, 115, 116, 87, 123, 128, 27, 29, 
+	31, 5, 47, 34, 42, 30, 1, 32, 
+	36, 0, 35, 37, 40, 41, 3, 43, 
+	4, 44, 27, 49, 51, 12, 71, 57, 
+	64, 50, 6, 52, 66, 59, 53, 11, 
+	70, 54, 7, 58, 60, 62, 63, 9, 
+	65, 10, 67, 27, 74, 17, 76, 89, 
+	81, 13, 92, 14, 80, 82, 84, 85, 
+	16, 88, 18, 90, 27, 27, 96, 98, 
+	19, 23, 102, 110, 97, 99, 112, 104, 
+	20, 103, 105, 107, 108, 22, 111, 24, 
+	113, 117, 118, 122, 119, 120, 25, 121, 
+	27, 124, 26, 126, 127
+};
+
+static const char _indic_syllable_machine_trans_actions[] = {
+	1, 0, 2, 0, 2, 2, 2, 3, 
+	2, 0, 2, 0, 2, 2, 2, 4, 
+	2, 0, 5, 0, 5, 6, 2, 7, 
+	2, 0, 2, 0, 2, 2, 8, 0, 
+	11, 2, 2, 5, 0, 12, 12, 0, 
+	2, 5, 2, 5, 2, 0, 13, 2, 
+	0, 0, 2, 0, 2, 2, 0, 2, 
+	2, 0, 0, 2, 2, 2, 0, 0, 
+	0, 2, 14, 2, 0, 0, 2, 0, 
+	2, 2, 0, 2, 2, 2, 2, 0, 
+	2, 2, 0, 0, 2, 2, 2, 0, 
+	0, 0, 2, 15, 5, 0, 5, 2, 
+	2, 0, 5, 0, 0, 2, 5, 5, 
+	0, 0, 0, 2, 16, 17, 2, 0, 
+	0, 0, 0, 2, 2, 2, 2, 2, 
+	0, 0, 2, 2, 2, 0, 0, 0, 
+	2, 0, 18, 18, 0, 0, 0, 0, 
+	19, 2, 0, 0, 0
+};
+
+static const char _indic_syllable_machine_to_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 9, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0
+};
+
+static const char _indic_syllable_machine_from_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 10, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0
+};
+
+static const short _indic_syllable_machine_eof_trans[] = {
+	1, 1, 1, 1, 1, 1, 8, 8, 
+	8, 8, 8, 8, 8, 16, 16, 22, 
+	16, 16, 16, 24, 24, 24, 24, 24, 
+	24, 1, 31, 0, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	67, 67, 67, 67, 67, 67, 67, 67, 
+	67, 67, 67, 67, 67, 67, 67, 67, 
+	67, 67, 67, 67, 67, 67, 67, 67, 
+	67, 92, 92, 92, 92, 92, 92, 92, 
+	92, 92, 92, 92, 92, 92, 92, 92, 
+	92, 92, 92, 92, 92, 109, 109, 110, 
+	110, 110, 110, 110, 110, 110, 110, 110, 
+	110, 110, 110, 110, 110, 110, 110, 110, 
+	110, 110, 110, 92, 47, 47, 47, 47, 
+	47, 47, 47, 137, 137, 137, 137, 137, 
+	109
+};
+
+static const int indic_syllable_machine_start = 27;
+static const int indic_syllable_machine_first_final = 27;
+static const int indic_syllable_machine_error = -1;
+
+static const int indic_syllable_machine_en_main = 27;
+
+
+#line 58 "hb-ot-shaper-indic-machine.rl"
+
+
+
+#line 117 "hb-ot-shaper-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) | syllable_type; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+inline void
+find_syllables_indic (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts, te, act;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 426 "hb-ot-shaper-indic-machine.hh"
+	{
+	cs = indic_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 137 "hb-ot-shaper-indic-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int syllable_serial = 1;
+  
+#line 442 "hb-ot-shaper-indic-machine.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const unsigned char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _indic_syllable_machine_from_state_actions[cs] ) {
+	case 10:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 456 "hb-ot-shaper-indic-machine.hh"
+	}
+
+	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
+	_inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs];
+
+	_slen = _indic_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) &&
+		( info[p].indic_category()) <= _keys[1] ?
+		( info[p].indic_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _indic_syllable_machine_trans_targs[_trans];
+
+	if ( _indic_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _indic_syllable_machine_trans_actions[_trans] ) {
+	case 2:
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 11:
+#line 113 "hb-ot-shaper-indic-machine.rl"
+	{te = p+1;{ found_syllable (indic_non_indic_cluster); }}
+	break;
+	case 13:
+#line 108 "hb-ot-shaper-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_consonant_syllable); }}
+	break;
+	case 14:
+#line 109 "hb-ot-shaper-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_vowel_syllable); }}
+	break;
+	case 17:
+#line 110 "hb-ot-shaper-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_standalone_cluster); }}
+	break;
+	case 19:
+#line 111 "hb-ot-shaper-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_symbol_cluster); }}
+	break;
+	case 15:
+#line 112 "hb-ot-shaper-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 16:
+#line 113 "hb-ot-shaper-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_non_indic_cluster); }}
+	break;
+	case 1:
+#line 108 "hb-ot-shaper-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_consonant_syllable); }}
+	break;
+	case 3:
+#line 109 "hb-ot-shaper-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_vowel_syllable); }}
+	break;
+	case 7:
+#line 110 "hb-ot-shaper-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_standalone_cluster); }}
+	break;
+	case 8:
+#line 111 "hb-ot-shaper-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_symbol_cluster); }}
+	break;
+	case 4:
+#line 112 "hb-ot-shaper-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 6:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 1:
+	{{p = ((te))-1;} found_syllable (indic_consonant_syllable); }
+	break;
+	case 5:
+	{{p = ((te))-1;} found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }
+	break;
+	case 6:
+	{{p = ((te))-1;} found_syllable (indic_non_indic_cluster); }
+	break;
+	}
+	}
+	break;
+	case 18:
+#line 1 "NONE"
+	{te = p+1;}
+#line 108 "hb-ot-shaper-indic-machine.rl"
+	{act = 1;}
+	break;
+	case 5:
+#line 1 "NONE"
+	{te = p+1;}
+#line 112 "hb-ot-shaper-indic-machine.rl"
+	{act = 5;}
+	break;
+	case 12:
+#line 1 "NONE"
+	{te = p+1;}
+#line 113 "hb-ot-shaper-indic-machine.rl"
+	{act = 6;}
+	break;
+#line 559 "hb-ot-shaper-indic-machine.hh"
+	}
+
+_again:
+	switch ( _indic_syllable_machine_to_state_actions[cs] ) {
+	case 9:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 568 "hb-ot-shaper-indic-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _indic_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _indic_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 145 "hb-ot-shaper-indic-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPER_INDIC_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-machine.rl b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-machine.rl
similarity index 78%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-machine.rl
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-machine.rl
index df9583f32d4fb1f983ee6de0b5bd3096508f6089..3274a776af0bdc07280de579e6ad3ff4db1a84da 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic-machine.rl
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-machine.rl
@@ -24,11 +24,23 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
+#ifndef HB_OT_SHAPER_INDIC_MACHINE_HH
+#define HB_OT_SHAPER_INDIC_MACHINE_HH
 
 #include "hb.hh"
 
+#include "hb-ot-layout.hh"
+#include "hb-ot-shaper-indic.hh"
+
+/* buffer var allocations */
+#define indic_category() ot_shaper_var_u8_category() /* indic_category_t */
+#define indic_position() ot_shaper_var_u8_auxiliary() /* indic_position_t */
+
+using indic_category_t = unsigned;
+using indic_position_t = ot_position_t;
+
+#define I_Cat(Cat) indic_syllable_machine_ex_##Cat
+
 enum indic_syllable_type_t {
   indic_consonant_syllable,
   indic_vowel_syllable,
@@ -47,6 +59,8 @@ enum indic_syllable_type_t {
 
 %%{
 
+
+export X    = 0;
 export C    = 1;
 export V    = 2;
 export N    = 3;
@@ -55,15 +69,17 @@ 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;
+export A    = 9;
+export VD   = 9;
+export PLACEHOLDER = 10;
+export DOTTEDCIRCLE = 11;
+export RS    = 12;
+export Repha = 14;
+export Ra    = 15;
+export CM    = 16;
+export Symbol= 17;
+export CS    = 18;
+
 
 c = (C | Ra);			# is_consonant
 n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
@@ -71,10 +87,9 @@ z = ZWJ|ZWNJ;			# is_joiner
 reph = (Ra H | Repha);		# possible reph
 
 cn = c.ZWJ?.n?;
-forced_rakar = ZWJ H ZWJ Ra;
 symbol = Symbol.N?;
-matra_group = z*.M.N?.(H | forced_rakar)?;
-syllable_tail = (z?.SM.SM?.ZWNJ?)? A*;
+matra_group = z*.M.N?.H?;
+syllable_tail = (z?.SM.SM?.ZWNJ?)? (A | VD)*;
 halant_group = (z?.H.(ZWJ.N?)?);
 final_halant_group = halant_group | H.ZWNJ;
 medial_group = CM?;
@@ -94,7 +109,7 @@ main := |*
 	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); };
+	broken_cluster		=> { found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; };
 	other			=> { found_syllable (indic_non_indic_cluster); };
 *|;
 
@@ -110,7 +125,7 @@ main := |*
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
-static void
+inline void
 find_syllables_indic (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
@@ -132,4 +147,4 @@ find_syllables_indic (hb_buffer_t *buffer)
 
 #undef found_syllable
 
-#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
+#endif /* HB_OT_SHAPER_INDIC_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-table.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-table.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8994f3ca59d432e8e3ce9787ce2263dd56c22664
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic-table.cc
@@ -0,0 +1,560 @@
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ *   ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt
+ *
+ * on files with these headers:
+ *
+ * # IndicSyllabicCategory-14.0.0.txt
+ * # Date: 2021-05-22, 01:01:00 GMT [KW, RP]
+ * # IndicPositionalCategory-14.0.0.txt
+ * # Date: 2021-05-22, 01:01:00 GMT [KW, RP]
+ * # Blocks-14.0.0.txt
+ * # Date: 2021-01-22, 23:29:00 GMT [KW]
+ */
+
+#include "hb.hh"
+
+#ifndef HB_NO_OT_SHAPE
+
+#include "hb-ot-shaper-indic.hh"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-macros"
+
+#include "hb-ot-shaper-indic-machine.hh"
+#include "hb-ot-shaper-khmer-machine.hh"
+#include "hb-ot-shaper-myanmar-machine.hh"
+
+/* indic */
+#define OT_X I_Cat(X)
+#define OT_C I_Cat(C)
+#define OT_V I_Cat(V)
+#define OT_N I_Cat(N)
+#define OT_H I_Cat(H)
+#define OT_ZWNJ I_Cat(ZWNJ)
+#define OT_ZWJ I_Cat(ZWJ)
+#define OT_M I_Cat(M)
+#define OT_SM I_Cat(SM)
+#define OT_A I_Cat(A)
+#define OT_VD I_Cat(VD)
+#define OT_PLACEHOLDER I_Cat(PLACEHOLDER)
+#define OT_DOTTEDCIRCLE I_Cat(DOTTEDCIRCLE)
+#define OT_RS I_Cat(RS)
+#define OT_Repha I_Cat(Repha)
+#define OT_Ra I_Cat(Ra)
+#define OT_CM I_Cat(CM)
+#define OT_Symbol I_Cat(Symbol)
+#define OT_CS I_Cat(CS)
+/* khmer */
+#define OT_VAbv K_Cat(VAbv)
+#define OT_VBlw K_Cat(VBlw)
+#define OT_VPre K_Cat(VPre)
+#define OT_VPst K_Cat(VPst)
+#define OT_Robatic K_Cat(Robatic)
+#define OT_Xgroup K_Cat(Xgroup)
+#define OT_Ygroup K_Cat(Ygroup)
+/* myanmar */
+static_assert (OT_VAbv == M_Cat(VAbv), "");
+static_assert (OT_VBlw == M_Cat(VBlw), "");
+static_assert (OT_VPre == M_Cat(VPre), "");
+static_assert (OT_VPst == M_Cat(VPst), "");
+#define OT_IV M_Cat(IV)
+#define OT_As M_Cat(As)
+#define OT_DB M_Cat(DB)
+#define OT_GB M_Cat(GB)
+#define OT_MH M_Cat(MH)
+#define OT_MR M_Cat(MR)
+#define OT_MW M_Cat(MW)
+#define OT_MY M_Cat(MY)
+#define OT_PT M_Cat(PT)
+#define OT_VS M_Cat(VS)
+#define OT_ML M_Cat(ML)
+
+
+#define _OT_A    OT_A            /*  53 chars; A */
+#define _OT_As   OT_As           /*   1 chars; As */
+#define _OT_C    OT_C            /* 478 chars; C */
+#define _OT_CM   OT_CM           /*   1 chars; CM */
+#define _OT_CS   OT_CS           /*   2 chars; CS */
+#define _OT_DC   OT_DOTTEDCIRCLE /*   1 chars; DOTTEDCIRCLE */
+#define _OT_H    OT_H            /*  11 chars; H */
+#define _OT_M    OT_M            /* 143 chars; M */
+#define _OT_MH   OT_MH           /*   1 chars; MH */
+#define _OT_ML   OT_ML           /*   1 chars; ML */
+#define _OT_MR   OT_MR           /*   1 chars; MR */
+#define _OT_MW   OT_MW           /*   2 chars; MW */
+#define _OT_MY   OT_MY           /*   3 chars; MY */
+#define _OT_N    OT_N            /*  17 chars; N */
+#define _OT_GB   OT_PLACEHOLDER  /* 165 chars; PLACEHOLDER */
+#define _OT_PT   OT_PT           /*   8 chars; PT */
+#define _OT_R    OT_Ra           /*  14 chars; Ra */
+#define _OT_Rf   OT_Repha        /*   1 chars; Repha */
+#define _OT_Rt   OT_Robatic      /*   3 chars; Robatic */
+#define _OT_SM   OT_SM           /*  55 chars; SM */
+#define _OT_S    OT_Symbol       /*  22 chars; Symbol */
+#define _OT_V    OT_V            /* 172 chars; V */
+#define _OT_VA   OT_VAbv         /*  18 chars; VAbv */
+#define _OT_VB   OT_VBlw         /*   7 chars; VBlw */
+#define _OT_VL   OT_VPre         /*   5 chars; VPre */
+#define _OT_VR   OT_VPst         /*  13 chars; VPst */
+#define _OT_VS   OT_VS           /*  16 chars; VS */
+#define _OT_X    OT_X            /*   2 chars; X */
+#define _OT_Xg   OT_Xgroup       /*   7 chars; Xgroup */
+#define _OT_Yg   OT_Ygroup       /*   4 chars; Ygroup */
+#define _OT_ZWJ  OT_ZWJ          /*   1 chars; ZWJ */
+#define _OT_ZWNJ OT_ZWNJ         /*   1 chars; ZWNJ */
+
+#define _POS_T   POS_ABOVE_C     /*  22 chars; ABOVE_C */
+#define _POS_A   POS_AFTER_MAIN  /*   3 chars; AFTER_MAIN */
+#define _POS_AP  POS_AFTER_POST  /*  50 chars; AFTER_POST */
+#define _POS_AS  POS_AFTER_SUB   /*  51 chars; AFTER_SUB */
+#define _POS_C   POS_BASE_C      /* 833 chars; BASE_C */
+#define _POS_BS  POS_BEFORE_SUB  /*  25 chars; BEFORE_SUB */
+#define _POS_B   POS_BELOW_C     /*  13 chars; BELOW_C */
+#define _POS_X   POS_END         /*  71 chars; END */
+#define _POS_R   POS_POST_C      /*  13 chars; POST_C */
+#define _POS_L   POS_PRE_C       /*   5 chars; PRE_C */
+#define _POS_LM  POS_PRE_M       /*  14 chars; PRE_M */
+#define _POS_SM  POS_SMVD        /* 129 chars; SMVD */
+
+#pragma GCC diagnostic pop
+
+#define INDIC_COMBINE_CATEGORIES(S,M) ((S) | ((M) << 8))
+
+#define _(S,M) INDIC_COMBINE_CATEGORIES (_OT_##S, _POS_##M)
+
+
+static const uint16_t indic_table[] = {
+
+
+#define indic_offset_0x0028u 0
+
+
+  /* Basic Latin */
+
+  /* 0028 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(GB,C),  _(X,X),  _(X,X),
+  /* 0030 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0038 */ _(GB,C), _(GB,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+#define indic_offset_0x00b0u 24
+
+
+  /* Latin-1 Supplement */
+
+  /* 00B0 */  _(X,X),  _(X,X),_(SM,SM),_(SM,SM),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 00B8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 00C0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 00C8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 00D0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(GB,C),
+
+#define indic_offset_0x0900u 64
+
+
+  /* Devanagari */
+
+  /* 0900 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),  _(V,C),  _(V,C),  _(V,C),  _(V,C),
+  /* 0908 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),
+  /* 0910 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0918 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0920 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0928 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0930 */  _(R,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0938 */  _(C,C),  _(C,C), _(M,AS), _(M,AS),  _(N,X), _(S,SM), _(M,AS), _(M,LM),
+  /* 0940 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS),
+  /* 0948 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS),  _(H,B), _(M,LM), _(M,AS),
+  /* 0950 */  _(X,X), _(A,SM), _(A,SM),_(SM,SM),_(SM,SM), _(M,AS), _(M,AS), _(M,AS),
+  /* 0958 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0960 */  _(V,C),  _(V,C), _(M,AS), _(M,AS),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0968 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0970 */  _(X,X),  _(X,X),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),
+  /* 0978 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+
+  /* Bengali */
+
+  /* 0980 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM),  _(X,X),  _(V,C),  _(V,C),  _(V,C),
+  /* 0988 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(X,X),  _(V,C),
+  /* 0990 */  _(V,C),  _(X,X),  _(X,X),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0998 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 09A0 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 09A8 */  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 09B0 */  _(R,C),  _(X,X),  _(C,C),  _(X,X),  _(X,X),  _(X,X),  _(C,C),  _(C,C),
+  /* 09B8 */  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(N,X), _(S,SM), _(M,AP), _(M,LM),
+  /* 09C0 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS),  _(X,X),  _(X,X), _(M,LM),
+  /* 09C8 */ _(M,LM),  _(X,X),  _(X,X), _(M,AP), _(M,AP),  _(H,B),  _(C,C),  _(X,X),
+  /* 09D0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(M,AP),
+  /* 09D8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(C,C),  _(C,C),  _(X,X),  _(C,C),
+  /* 09E0 */  _(V,C),  _(V,C), _(M,AS), _(M,AS),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 09E8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 09F0 */  _(R,C),  _(C,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 09F8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(GB,C),  _(X,X),_(SM,SM),  _(X,X),
+
+  /* Gurmukhi */
+
+  /* 0A00 */  _(X,X),_(SM,SM),_(SM,SM),_(SM,SM),  _(X,X),  _(V,C),  _(V,C),  _(V,C),
+  /* 0A08 */  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(V,C),
+  /* 0A10 */  _(V,C),  _(X,X),  _(X,X),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0A18 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0A20 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0A28 */  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0A30 */  _(R,C),  _(X,X),  _(C,C),  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(X,X),
+  /* 0A38 */  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(N,X),  _(X,X), _(M,AP), _(M,LM),
+  /* 0A40 */ _(M,AP), _(M,AP), _(M,AP),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(M,AP),
+  /* 0A48 */ _(M,AP),  _(X,X),  _(X,X), _(M,AP), _(M,AP),  _(H,B),  _(X,X),  _(X,X),
+  /* 0A50 */  _(X,X),  _(M,B),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0A58 */  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(X,X),  _(C,C),  _(X,X),
+  /* 0A60 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0A68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0A70 */_(SM,SM),_(SM,SM),  _(C,C),  _(C,C),  _(X,X), _(CM,C),  _(X,X),  _(X,X),
+  /* 0A78 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+  /* Gujarati */
+
+  /* 0A80 */  _(X,X),_(SM,SM),_(SM,SM),_(SM,SM),  _(X,X),  _(V,C),  _(V,C),  _(V,C),
+  /* 0A88 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(V,C),
+  /* 0A90 */  _(V,C),  _(V,C),  _(X,X),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0A98 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0AA0 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0AA8 */  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0AB0 */  _(R,C),  _(X,X),  _(C,C),  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),
+  /* 0AB8 */  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(N,X), _(S,SM), _(M,AP), _(M,LM),
+  /* 0AC0 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AS),  _(X,X), _(M,AS),
+  /* 0AC8 */ _(M,AS), _(M,AP),  _(X,X), _(M,AP), _(M,AP),  _(H,B),  _(X,X),  _(X,X),
+  /* 0AD0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0AD8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0AE0 */  _(V,C),  _(V,C), _(M,AP), _(M,AP),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0AE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0AF0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0AF8 */  _(X,X),  _(C,C), _(A,SM),  _(N,X), _(A,SM),  _(N,X),  _(N,X),  _(N,X),
+
+  /* Oriya */
+
+  /* 0B00 */  _(X,X),_(SM,BS),_(SM,SM),_(SM,SM),  _(X,X),  _(V,C),  _(V,C),  _(V,C),
+  /* 0B08 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(X,X),  _(V,C),
+  /* 0B10 */  _(V,C),  _(X,X),  _(X,X),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0B18 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0B20 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0B28 */  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0B30 */  _(R,C),  _(X,X),  _(C,C),  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),
+  /* 0B38 */  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(N,X), _(S,SM), _(M,AP),  _(M,A),
+  /* 0B40 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS),  _(X,X),  _(X,X), _(M,LM),
+  /* 0B48 */  _(M,A),  _(X,X),  _(X,X), _(M,AP), _(M,AP),  _(H,B),  _(X,X),  _(X,X),
+  /* 0B50 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(N,X),  _(M,A), _(M,AP),
+  /* 0B58 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(C,C),  _(C,C),  _(X,X),  _(C,C),
+  /* 0B60 */  _(V,C),  _(V,C), _(M,AS), _(M,AS),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0B68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0B70 */  _(X,X),  _(C,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0B78 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+  /* Tamil */
+
+  /* 0B80 */  _(X,X),  _(X,X),_(SM,SM),  _(X,X),  _(X,X),  _(V,C),  _(V,C),  _(V,C),
+  /* 0B88 */  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(X,X),  _(X,X),  _(V,C),  _(V,C),
+  /* 0B90 */  _(V,C),  _(X,X),  _(V,C),  _(V,C),  _(V,C),  _(C,C),  _(X,X),  _(X,X),
+  /* 0B98 */  _(X,X),  _(C,C),  _(C,C),  _(X,X),  _(C,C),  _(X,X),  _(C,C),  _(C,C),
+  /* 0BA0 */  _(X,X),  _(X,X),  _(X,X),  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(X,X),
+  /* 0BA8 */  _(C,C),  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(X,X),  _(C,C),  _(C,C),
+  /* 0BB0 */  _(R,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0BB8 */  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(M,AP), _(M,AP),
+  /* 0BC0 */ _(M,AS), _(M,AP), _(M,AP),  _(X,X),  _(X,X),  _(X,X), _(M,LM), _(M,LM),
+  /* 0BC8 */ _(M,LM),  _(X,X), _(M,AP), _(M,AP), _(M,AP),  _(H,T),  _(X,X),  _(X,X),
+  /* 0BD0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(M,AP),
+  /* 0BD8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0BE0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0BE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0BF0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0BF8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+  /* Telugu */
+
+  /* 0C00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),  _(V,C),  _(V,C),  _(V,C),
+  /* 0C08 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(V,C),  _(V,C),
+  /* 0C10 */  _(V,C),  _(X,X),  _(V,C),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0C18 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0C20 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0C28 */  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0C30 */  _(R,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0C38 */  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(N,X), _(S,SM), _(M,BS), _(M,BS),
+  /* 0C40 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS),  _(X,X), _(M,BS), _(M,BS),
+  /* 0C48 */ _(M,BS),  _(X,X), _(M,BS), _(M,BS), _(M,BS),  _(H,T),  _(X,X),  _(X,X),
+  /* 0C50 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(M,BS), _(M,BS),  _(X,X),
+  /* 0C58 */  _(C,C),  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(C,C),  _(X,X),  _(X,X),
+  /* 0C60 */  _(V,C),  _(V,C), _(M,BS), _(M,BS),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0C68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0C70 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0C78 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+  /* Kannada */
+
+  /* 0C80 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM),  _(X,X),  _(V,C),  _(V,C),  _(V,C),
+  /* 0C88 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(V,C),  _(V,C),
+  /* 0C90 */  _(V,C),  _(X,X),  _(V,C),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0C98 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0CA0 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0CA8 */  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0CB0 */  _(R,C),  _(C,C),  _(C,C),  _(C,C),  _(X,X),  _(C,C),  _(C,C),  _(C,C),
+  /* 0CB8 */  _(C,C),  _(C,C),  _(X,X),  _(X,X),  _(N,X), _(S,SM), _(M,BS), _(M,BS),
+  /* 0CC0 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS),  _(X,X), _(M,BS), _(M,AS),
+  /* 0CC8 */ _(M,AS),  _(X,X), _(M,AS), _(M,AS), _(M,BS),  _(H,T),  _(X,X),  _(X,X),
+  /* 0CD0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X), _(M,AS), _(M,AS),  _(X,X),
+  /* 0CD8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(C,C),  _(C,C),  _(X,X),
+  /* 0CE0 */  _(V,C),  _(V,C), _(M,BS), _(M,BS),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0CE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0CF0 */  _(X,X), _(CS,C), _(CS,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0CF8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+  /* Malayalam */
+
+  /* 0D00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(GB,C),  _(V,C),  _(V,C),  _(V,C),
+  /* 0D08 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(V,C),  _(V,C),
+  /* 0D10 */  _(V,C),  _(X,X),  _(V,C),  _(V,C),  _(V,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0D18 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0D20 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0D28 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0D30 */  _(R,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 0D38 */  _(C,C),  _(C,C),  _(C,C), _(M,AS), _(M,AS), _(S,SM), _(M,AP), _(M,AP),
+  /* 0D40 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP),  _(X,X), _(M,LM), _(M,LM),
+  /* 0D48 */ _(M,LM),  _(X,X), _(M,AP), _(M,AP), _(M,AP),  _(H,T), _(Rf,X),  _(X,X),
+  /* 0D50 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(C,C),  _(C,C),  _(C,C), _(M,AP),
+  /* 0D58 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(V,C),
+  /* 0D60 */  _(V,C),  _(V,C), _(M,AP), _(M,AP),  _(X,X),  _(X,X), _(GB,C), _(GB,C),
+  /* 0D68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 0D70 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 0D78 */  _(X,X),  _(X,X),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+
+#define indic_offset_0x1000u 1216
+
+
+  /* Myanmar */
+
+  /* 1000 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(R,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1008 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1010 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1018 */  _(C,C),  _(C,C),  _(C,C),  _(R,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1020 */  _(C,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),
+  /* 1028 */  _(V,C),  _(V,C),  _(V,C), _(VR,R), _(VR,R), _(VA,T), _(VA,T), _(VB,B),
+  /* 1030 */ _(VB,B), _(VL,L), _(A,SM), _(VA,T), _(VA,T), _(VA,T), _(A,SM),  _(N,X),
+  /* 1038 */_(SM,SM),  _(H,X), _(As,X), _(MY,X), _(MR,X), _(MW,X), _(MH,X),  _(C,C),
+  /* 1040 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 1048 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C),  _(X,X),  _(X,X),  _(C,C),  _(X,X),
+  /* 1050 */  _(C,C),  _(C,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C), _(VR,R), _(VR,R),
+  /* 1058 */ _(VB,B), _(VB,B),  _(R,C),  _(C,C),  _(C,C),  _(C,C), _(MY,X), _(MY,X),
+  /* 1060 */ _(ML,X),  _(C,C), _(VR,R), _(PT,X), _(PT,X),  _(C,C),  _(C,C), _(VR,R),
+  /* 1068 */ _(VR,R), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(PT,X),  _(C,C),  _(C,C),
+  /* 1070 */  _(C,C), _(VA,T), _(VA,T), _(VA,T), _(VA,T),  _(C,C),  _(C,C),  _(C,C),
+  /* 1078 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1080 */  _(C,C),  _(C,C), _(MW,X), _(VR,R), _(VL,L), _(VA,T), _(VA,T),_(SM,SM),
+  /* 1088 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),  _(C,C),_(SM,SM),
+  /* 1090 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 1098 */ _(GB,C), _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(VA,T),  _(X,X),  _(X,X),
+
+#define indic_offset_0x1780u 1376
+
+
+  /* Khmer */
+
+  /* 1780 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1788 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1790 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 1798 */  _(C,C),  _(C,C),  _(R,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* 17A0 */  _(C,C),  _(C,C),  _(C,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),
+  /* 17A8 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(V,C),
+  /* 17B0 */  _(V,C),  _(V,C),  _(V,C),  _(V,C),  _(X,X),  _(X,X), _(VR,R), _(VA,T),
+  /* 17B8 */ _(VA,T), _(VA,T), _(VA,T), _(VB,B), _(VB,B), _(VB,B), _(VA,T), _(VR,R),
+  /* 17C0 */ _(VR,R), _(VL,L), _(VL,L), _(VL,L), _(VR,R), _(VR,R), _(Xg,X), _(Yg,X),
+  /* 17C8 */ _(Yg,X), _(Rt,X), _(Rt,X), _(Xg,X), _(Rt,X), _(Xg,X), _(Xg,X), _(Xg,X),
+  /* 17D0 */ _(Xg,X), _(Xg,X),  _(H,X), _(Yg,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 17D8 */  _(X,X), _(GB,C),  _(X,X),  _(X,X), _(S,SM), _(Yg,X),  _(X,X),  _(X,X),
+  /* 17E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* 17E8 */ _(GB,C), _(GB,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+#define indic_offset_0x1cd0u 1488
+
+
+  /* Vedic Extensions */
+
+  /* 1CD0 */ _(A,SM), _(A,SM), _(A,SM),  _(X,X), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
+  /* 1CD8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
+  /* 1CE0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
+  /* 1CE8 */ _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(A,SM), _(S,SM), _(S,SM),
+  /* 1CF0 */ _(S,SM), _(S,SM),  _(C,C),  _(C,C), _(A,SM),  _(C,C),  _(C,C), _(A,SM),
+  /* 1CF8 */ _(A,SM), _(A,SM), _(GB,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+#define indic_offset_0x2008u 1536
+
+
+  /* General Punctuation */
+
+  /* 2008 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),_(ZWNJ,X),_(ZWJ,X),  _(X,X),  _(X,X),
+  /* 2010 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),  _(X,X),  _(X,X),
+  /* 2018 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 2020 */  _(X,X),  _(X,X), _(GB,C),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+
+#define indic_offset_0x2070u 1568
+
+
+  /* Superscripts and Subscripts */
+
+  /* 2070 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),_(SM,SM),  _(X,X),  _(X,X),  _(X,X),
+  /* 2078 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 2080 */  _(X,X),  _(X,X),_(SM,SM),_(SM,SM),_(SM,SM),  _(X,X),  _(X,X),  _(X,X),
+
+#define indic_offset_0x25f8u 1592
+
+
+  /* Geometric Shapes */
+
+  /* 25F8 */  _(X,X),  _(X,X),  _(X,X), _(GB,C), _(GB,C), _(GB,C), _(GB,C),  _(X,X),
+
+#define indic_offset_0xa8e0u 1600
+
+
+  /* Devanagari Extended */
+
+  /* A8E0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
+  /* A8E8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
+  /* A8F0 */ _(A,SM), _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM),
+  /* A8F8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(V,C), _(M,AS),
+
+#define indic_offset_0xa9e0u 1632
+
+
+  /* Myanmar Extended-B */
+
+  /* A9E0 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C), _(VA,T),  _(X,X),  _(C,C),
+  /* A9E8 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* A9F0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
+  /* A9F8 */ _(GB,C), _(GB,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(X,X),
+
+#define indic_offset_0xaa60u 1664
+
+
+  /* Myanmar Extended-A */
+
+  /* AA60 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* AA68 */  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),  _(C,C),
+  /* AA70 */  _(X,X),  _(C,C),  _(C,C),  _(C,C), _(GB,C), _(GB,C), _(GB,C),  _(X,X),
+  /* AA78 */  _(X,X),  _(X,X),  _(C,C), _(PT,X),  _(N,X),  _(N,X),  _(C,C),  _(C,C),
+
+#define indic_offset_0xfe00u 1696
+
+
+  /* Variation Selectors */
+
+  /* FE00 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X),
+  /* FE08 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X),
+
+#define indic_offset_0x11300u 1712
+
+
+  /* Grantha */
+
+  /* 11300 */  _(X,X),_(SM,SM),_(SM,SM),_(SM,SM),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 11308 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 11310 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 11318 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 11320 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 11328 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 11330 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 11338 */  _(X,X),  _(X,X),  _(X,X),  _(N,X),  _(N,X),  _(X,X),  _(X,X),  _(X,X),
+
+}; /* Table items: 1776; occupancy: 69% */
+
+uint16_t
+hb_indic_get_categories (hb_codepoint_t u)
+{
+  switch (u >> 12)
+  {
+    case 0x0u:
+      if (unlikely (u == 0x00A0u)) return _(GB,C);
+      if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0D7Fu)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
+      break;
+
+    case 0x1u:
+      if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
+      break;
+
+    case 0x2u:
+      if (unlikely (u == 0x25CCu)) return _(DC,C);
+      if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2027u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x25F8u, 0x25FFu)) return indic_table[u - 0x25F8u + indic_offset_0x25f8u];
+      break;
+
+    case 0xAu:
+      if (hb_in_range<hb_codepoint_t> (u, 0xA8E0u, 0xA8FFu)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
+      break;
+
+    case 0xFu:
+      if (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)) return indic_table[u - 0xFE00u + indic_offset_0xfe00u];
+      break;
+
+    case 0x11u:
+      if (hb_in_range<hb_codepoint_t> (u, 0x11300u, 0x1133Fu)) return indic_table[u - 0x11300u + indic_offset_0x11300u];
+      break;
+
+    default:
+      break;
+  }
+  return _(X,X);
+}
+
+#undef _
+#undef INDIC_COMBINE_CATEGORIES
+
+#undef _OT_A
+#undef _OT_As
+#undef _OT_C
+#undef _OT_CM
+#undef _OT_CS
+#undef _OT_DC
+#undef _OT_H
+#undef _OT_M
+#undef _OT_MH
+#undef _OT_ML
+#undef _OT_MR
+#undef _OT_MW
+#undef _OT_MY
+#undef _OT_N
+#undef _OT_GB
+#undef _OT_PT
+#undef _OT_R
+#undef _OT_Rf
+#undef _OT_Rt
+#undef _OT_SM
+#undef _OT_S
+#undef _OT_V
+#undef _OT_VA
+#undef _OT_VB
+#undef _OT_VL
+#undef _OT_VR
+#undef _OT_VS
+#undef _OT_X
+#undef _OT_Xg
+#undef _OT_Yg
+#undef _OT_ZWJ
+#undef _OT_ZWNJ
+
+#undef _POS_T
+#undef _POS_A
+#undef _POS_AP
+#undef _POS_AS
+#undef _POS_C
+#undef _POS_BS
+#undef _POS_B
+#undef _POS_X
+#undef _POS_R
+#undef _POS_L
+#undef _POS_LM
+#undef _POS_SM
+
+#endif
+
+/* == End of generated table == */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic.cc
similarity index 85%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic.cc
index c80f7df6a9c5329a02356ed5f05e88801e2cf60b..48a3c744630efe8e955939b985e808b247181966 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-indic.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic.cc
@@ -28,9 +28,9 @@
 
 #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-shaper-indic.hh"
+#include "hb-ot-shaper-indic-machine.hh"
+#include "hb-ot-shaper-vowel-constraints.hh"
 #include "hb-ot-layout.hh"
 
 
@@ -39,6 +39,81 @@
  */
 
 
+static inline void
+set_indic_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+
+  info.indic_category() = (indic_category_t) (type & 0xFFu);
+  info.indic_position() = (indic_position_t) (type >> 8);
+}
+
+
+static inline bool
+is_one_of (const hb_glyph_info_t &info, unsigned int flags)
+{
+  /* If it ligated, all bets are off. */
+  if (_hb_glyph_info_ligated (&info)) return false;
+  return !!(FLAG_UNSAFE (info.indic_category()) & flags);
+}
+
+/* Note:
+ *
+ * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
+ * cannot happen in a consonant syllable.  The plus side however is, we can call the
+ * consonant syllable logic from the vowel syllable function and get it all right!
+ *
+ * Keep in sync with consonant_categories in the generator. */
+#define CONSONANT_FLAGS_INDIC (FLAG (I_Cat(C)) | FLAG (I_Cat(CS)) | FLAG (I_Cat(Ra)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(V)) | FLAG (I_Cat(PLACEHOLDER)) | FLAG (I_Cat(DOTTEDCIRCLE)))
+
+static inline bool
+is_consonant (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, CONSONANT_FLAGS_INDIC);
+}
+
+#define JOINER_FLAGS (FLAG (I_Cat(ZWJ)) | FLAG (I_Cat(ZWNJ)))
+
+static inline bool
+is_joiner (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, JOINER_FLAGS);
+}
+
+static inline bool
+is_halant (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, FLAG (I_Cat(H)));
+}
+
+struct hb_indic_would_substitute_feature_t
+{
+  void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
+  {
+    zero_context = zero_context_;
+    map->get_stage_lookups (0/*GSUB*/,
+			    map->get_feature_stage (0/*GSUB*/, feature_tag),
+			    &lookups, &count);
+  }
+
+  bool would_substitute (const hb_codepoint_t *glyphs,
+			 unsigned int          glyphs_count,
+			 hb_face_t            *face) const
+  {
+    for (unsigned int i = 0; i < count; i++)
+      if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context))
+	return true;
+    return false;
+  }
+
+  private:
+  const hb_ot_map_t::lookup_map_t *lookups;
+  unsigned int count;
+  bool zero_context;
+};
+
+
 /*
  * Indic configurations.  Note that we do not want to keep every single script-specific
  * behavior in these tables necessarily.  This should mainly be used for per-script
@@ -47,10 +122,6 @@
  * instead of adding a new flag in these structs.
  */
 
-enum base_position_t {
-  BASE_POS_LAST_SINHALA,
-  BASE_POS_LAST
-};
 enum reph_position_t {
   REPH_POS_AFTER_MAIN  = POS_AFTER_MAIN,
   REPH_POS_BEFORE_SUB  = POS_BEFORE_SUB,
@@ -72,7 +143,6 @@ struct indic_config_t
   hb_script_t     script;
   bool            has_old_spec;
   hb_codepoint_t  virama;
-  base_position_t base_pos;
   reph_position_t reph_pos;
   reph_mode_t     reph_mode;
   blwf_mode_t     blwf_mode;
@@ -81,26 +151,19 @@ struct indic_config_t
 static const indic_config_t indic_configs[] =
 {
   /* Default.  Should be first. */
-  {HB_SCRIPT_INVALID,	false,      0,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_DEVANAGARI,true, 0x094Du,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_BENGALI,	true, 0x09CDu,BASE_POS_LAST, REPH_POS_AFTER_SUB,  REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_GURMUKHI,	true, 0x0A4Du,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_GUJARATI,	true, 0x0ACDu,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_ORIYA,	true, 0x0B4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_TAMIL,	true, 0x0BCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_TELUGU,	true, 0x0C4Du,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY},
-  {HB_SCRIPT_KANNADA,	true, 0x0CCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY},
-  {HB_SCRIPT_MALAYALAM,	true, 0x0D4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_SINHALA,	false,0x0DCAu,BASE_POS_LAST_SINHALA,
-						     REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_INVALID,	false,      0,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_DEVANAGARI,true, 0x094Du,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_BENGALI,	true, 0x09CDu,REPH_POS_AFTER_SUB,  REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_GURMUKHI,	true, 0x0A4Du,REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_GUJARATI,	true, 0x0ACDu,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_ORIYA,	true, 0x0B4Du,REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_TAMIL,	true, 0x0BCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+  {HB_SCRIPT_TELUGU,	true, 0x0C4Du,REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY},
+  {HB_SCRIPT_KANNADA,	true, 0x0CCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY},
+  {HB_SCRIPT_MALAYALAM,	true, 0x0D4Du,REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST},
 };
 
 
-
-/*
- * Indic shaper.
- */
-
 static const hb_ot_map_feature_t
 indic_features[] =
 {
@@ -207,6 +270,7 @@ static void
 override_features_indic (hb_ot_shape_planner_t *plan)
 {
   plan->map.disable_feature (HB_TAG('l','i','g','a'));
+  plan->map.add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var
 }
 
 
@@ -356,6 +420,7 @@ setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font HB_UNUSED,
 		       hb_buffer_t *buffer)
 {
+  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
   find_syllables_indic (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
@@ -367,7 +432,7 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
   int a = pa->indic_position();
   int b = pb->indic_position();
 
-  return a < b ? -1 : a == b ? 0 : +1;
+  return (int) a - (int) b;
 }
 
 
@@ -379,9 +444,6 @@ update_consonant_positions_indic (const hb_ot_shape_plan_t *plan,
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
 
-  if (indic_plan->config->base_pos != BASE_POS_LAST)
-    return;
-
   hb_codepoint_t virama;
   if (indic_plan->load_virama_glyph (font, &virama))
   {
@@ -416,9 +478,9 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
    */
   if (buffer->props.script == HB_SCRIPT_KANNADA &&
       start + 3 <= end &&
-      is_one_of (info[start  ], FLAG (OT_Ra)) &&
-      is_one_of (info[start+1], FLAG (OT_H)) &&
-      is_one_of (info[start+2], FLAG (OT_ZWJ)))
+      is_one_of (info[start  ], FLAG (I_Cat(Ra))) &&
+      is_one_of (info[start+1], FLAG (I_Cat(H))) &&
+      is_one_of (info[start+2], FLAG (I_Cat(ZWJ))))
   {
     buffer->merge_clusters (start+1, start+3);
     hb_glyph_info_t tmp = info[start+1];
@@ -452,7 +514,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
 	start + 3 <= end &&
 	(
 	 (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) ||
-	 (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == OT_ZWJ)
+	 (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == I_Cat(ZWJ))
 	))
     {
       /* See if it matches the 'rphf' feature. */
@@ -470,7 +532,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
 	base = start;
 	has_reph = true;
       }
-    } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == OT_Repha)
+    } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == I_Cat(Repha))
     {
 	limit += 1;
 	while (limit < end && is_joiner (info[limit]))
@@ -479,84 +541,51 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
 	has_reph = true;
     }
 
-    switch (indic_plan->config->base_pos)
     {
-      case BASE_POS_LAST:
-      {
-	/* -> starting from the end of the syllable, move backwards */
-	unsigned int i = end;
-	bool seen_below = false;
-	do {
-	  i--;
-	  /* -> until a consonant is found */
-	  if (is_consonant (info[i]))
+      /* -> starting from the end of the syllable, move backwards */
+      unsigned int i = end;
+      bool seen_below = false;
+      do {
+	i--;
+	/* -> until a consonant is found */
+	if (is_consonant (info[i]))
+	{
+	  /* -> that does not have a below-base or post-base form
+	   * (post-base forms have to follow below-base forms), */
+	  if (info[i].indic_position() != POS_BELOW_C &&
+	      (info[i].indic_position() != POS_POST_C || seen_below))
 	  {
-	    /* -> that does not have a below-base or post-base form
-	     * (post-base forms have to follow below-base forms), */
-	    if (info[i].indic_position() != POS_BELOW_C &&
-		(info[i].indic_position() != POS_POST_C || seen_below))
-	    {
-	      base = i;
-	      break;
-	    }
-	    if (info[i].indic_position() == POS_BELOW_C)
-	      seen_below = true;
-
-	    /* -> or that is not a pre-base-reordering Ra,
-	     *
-	     * IMPLEMENTATION NOTES:
-	     *
-	     * Our pre-base-reordering Ra's are marked POS_POST_C, so will be skipped
-	     * by the logic above already.
-	     */
-
-	    /* -> or arrive at the first consonant. The consonant stopped at will
-	     * be the base. */
 	    base = i;
+	    break;
 	  }
-	  else
-	  {
-	    /* A ZWJ after a Halant stops the base search, and requests an explicit
-	     * half form.
-	     * A ZWJ before a Halant, requests a subjoined form instead, and hence
-	     * search continues.  This is particularly important for Bengali
-	     * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */
-	    if (start < i &&
-		info[i].indic_category() == OT_ZWJ &&
-		info[i - 1].indic_category() == OT_H)
-	      break;
-	  }
-	} while (i > limit);
-      }
-      break;
-
-      case BASE_POS_LAST_SINHALA:
-      {
-	/* Sinhala base positioning is slightly different from main Indic, in that:
-	 * 1. Its ZWJ behavior is different,
-	 * 2. We don't need to look into the font for consonant positions.
-	 */
-
-	if (!has_reph)
-	  base = limit;
+	  if (info[i].indic_position() == POS_BELOW_C)
+	    seen_below = true;
 
-	/* Find the last base consonant that is not blocked by ZWJ.  If there is
-	 * a ZWJ right before a base consonant, that would request a subjoined form. */
-	for (unsigned int i = limit; i < end; i++)
-	  if (is_consonant (info[i]))
-	  {
-	    if (limit < i && info[i - 1].indic_category() == OT_ZWJ)
-	      break;
-	    else
-	      base = i;
-	  }
+	  /* -> or that is not a pre-base-reordering Ra,
+	   *
+	   * IMPLEMENTATION NOTES:
+	   *
+	   * Our pre-base-reordering Ra's are marked POS_POST_C, so will be skipped
+	   * by the logic above already.
+	   */
 
-	/* Mark all subsequent consonants as below. */
-	for (unsigned int i = base + 1; i < end; i++)
-	  if (is_consonant (info[i]))
-	    info[i].indic_position() = POS_BELOW_C;
-      }
-      break;
+	  /* -> or arrive at the first consonant. The consonant stopped at will
+	   * be the base. */
+	  base = i;
+	}
+	else
+	{
+	  /* A ZWJ after a Halant stops the base search, and requests an explicit
+	   * half form.
+	   * A ZWJ before a Halant, requests a subjoined form instead, and hence
+	   * search continues.  This is particularly important for Bengali
+	   * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */
+	  if (start < i &&
+	      info[i].indic_category() == I_Cat(ZWJ) &&
+	      info[i - 1].indic_category() == I_Cat(H))
+	    break;
+	}
+      } while (i > limit);
     }
 
     /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
@@ -611,18 +640,6 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
   if (base < end)
     info[base].indic_position() = POS_BASE_C;
 
-  /* Mark final consonants.  A final consonant is one appearing after a matra.
-   * Happens in Sinhala. */
-  for (unsigned int i = base + 1; i < end; i++)
-    if (info[i].indic_category() == OT_M) {
-      for (unsigned int j = i + 1; j < end; j++)
-	if (is_consonant (info[j])) {
-	 info[j].indic_position() = POS_FINAL_C;
-	 break;
-       }
-      break;
-    }
-
   /* Handle beginning Ra */
   if (has_reph)
     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
@@ -659,14 +676,14 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
   {
     bool disallow_double_halants = buffer->props.script == HB_SCRIPT_KANNADA;
     for (unsigned int i = base + 1; i < end; i++)
-      if (info[i].indic_category() == OT_H)
+      if (info[i].indic_category() == I_Cat(H))
       {
 	unsigned int j;
 	for (j = end - 1; j > i; j--)
 	  if (is_consonant (info[j]) ||
-	      (disallow_double_halants && info[j].indic_category() == OT_H))
+	      (disallow_double_halants && info[j].indic_category() == I_Cat(H)))
 	    break;
-	if (info[j].indic_category() != OT_H && j > i) {
+	if (info[j].indic_category() != I_Cat(H) && j > i) {
 	  /* Move Halant to after last consonant. */
 	  hb_glyph_info_t t = info[i];
 	  memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0]));
@@ -681,20 +698,16 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
     indic_position_t last_pos = POS_START;
     for (unsigned int i = start; i < end; i++)
     {
-      if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_H))))
+      if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (I_Cat(N)) | FLAG (I_Cat(RS)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(H)))))
       {
 	info[i].indic_position() = last_pos;
-	if (unlikely (info[i].indic_category() == OT_H &&
+	if (unlikely (info[i].indic_category() == I_Cat(H) &&
 		      info[i].indic_position() == POS_PRE_M))
 	{
 	  /*
 	   * Uniscribe doesn't move the Halant with Left Matra.
-	   * TEST: U+092B,U+093F,U+094DE
-	   * We follow.  This is important for the Sinhala
-	   * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
-	   * where U+0DD9 is a left matra and U+0DCA is the virama.
-	   * We don't want to move the virama with the left matra.
-	   * TEST: U+0D9A,U+0DDA
+	   * TEST: U+092B,U+093F,U+094D
+	   * We follow.
 	   */
 	  for (unsigned int j = i; j > start; j--)
 	    if (info[j - 1].indic_position() != POS_PRE_M) {
@@ -718,7 +731,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
 	  if (info[j].indic_position() < POS_SMVD)
 	    info[j].indic_position() = info[i].indic_position();
 	last = i;
-      } else if (info[i].indic_category() == OT_M)
+      } else if (info[i].indic_category() == I_Cat(M))
 	last = i;
   }
 
@@ -849,10 +862,10 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
      * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915
      */
     for (unsigned int i = start; i + 1 < base; i++)
-      if (info[i  ].indic_category() == OT_Ra &&
-	  info[i+1].indic_category() == OT_H  &&
+      if (info[i  ].indic_category() == I_Cat(Ra) &&
+	  info[i+1].indic_category() == I_Cat(H)  &&
 	  (i + 2 == base ||
-	   info[i+2].indic_category() != OT_ZWJ))
+	   info[i+2].indic_category() != I_Cat(ZWJ)))
       {
 	info[i  ].mask |= indic_plan->mask_array[INDIC_BLWF];
 	info[i+1].mask |= indic_plan->mask_array[INDIC_BLWF];
@@ -879,7 +892,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
   /* Apply ZWJ/ZWNJ effects */
   for (unsigned int i = start + 1; i < end; i++)
     if (is_joiner (info[i])) {
-      bool non_joiner = info[i].indic_category() == OT_ZWNJ;
+      bool non_joiner = info[i].indic_category() == I_Cat(ZWNJ);
       unsigned int j = i;
 
       do {
@@ -912,7 +925,7 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
     /* For dotted-circle, this is what Uniscribe does:
      * If dotted-circle is the last glyph, it just does nothing.
      * Ie. It doesn't form Reph. */
-    if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
+    if (buffer->info[end - 1].indic_category() == I_Cat(DOTTEDCIRCLE))
       return;
   }
 
@@ -955,8 +968,8 @@ initial_reordering_indic (const hb_ot_shape_plan_t *plan,
   update_consonant_positions_indic (plan, font, buffer);
   hb_syllabic_insert_dotted_circles (font, buffer,
 				     indic_broken_cluster,
-				     OT_DOTTEDCIRCLE,
-				     OT_Repha,
+				     I_Cat(DOTTEDCIRCLE),
+				     I_Cat(Repha),
 				     POS_END);
 
   foreach_syllable (buffer, start, end)
@@ -978,7 +991,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
    * and possibly multiple substitutions happened prior to this
    * phase, and that might have messed up our properties.  Recover
    * from a particular case of that where we're fairly sure that a
-   * class of OT_H is desired but has been lost. */
+   * class of I_Cat(H) is desired but has been lost. */
   /* We don't call load_virama_glyph(), since we know it's already
    * loaded. */
   hb_codepoint_t virama_glyph = indic_plan->virama_glyph.get_relaxed ();
@@ -990,7 +1003,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
 	  _hb_glyph_info_multiplied (&info[i]))
       {
 	/* This will make sure that this glyph passes is_halant() test. */
-	info[i].indic_category() = OT_H;
+	info[i].indic_category() = I_Cat(H);
 	_hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
       }
   }
@@ -1056,11 +1069,11 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
       break;
     }
   if (base == end && start < base &&
-      is_one_of (info[base - 1], FLAG (OT_ZWJ)))
+      is_one_of (info[base - 1], FLAG (I_Cat(ZWJ))))
     base--;
   if (base < end)
     while (start < base &&
-	   is_one_of (info[base], (FLAG (OT_N) | FLAG (OT_H))))
+	   is_one_of (info[base], (FLAG (I_Cat(N)) | FLAG (I_Cat(H)))))
       base--;
 
 
@@ -1105,7 +1118,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
     {
     search:
       while (new_pos > start &&
-	     !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_H)))))
+	     !(is_one_of (info[new_pos], (FLAG (I_Cat(M)) | FLAG (I_Cat(H))))))
 	new_pos--;
 
       /* If we found no Halant we are done.
@@ -1122,7 +1135,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
 	if (new_pos + 1 < end)
 	{
 	  /* -> If ZWJ follows this halant, matra is NOT repositioned after this halant. */
-	  if (info[new_pos + 1].indic_category() == OT_ZWJ)
+	  if (info[new_pos + 1].indic_category() == I_Cat(ZWJ))
 	  {
 	    /* Keep searching. */
 	    if (new_pos > start)
@@ -1195,7 +1208,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
    */
   if (start + 1 < end &&
       info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
-      ((info[start].indic_category() == OT_Repha) ^
+      ((info[start].indic_category() == I_Cat(Repha)) ^
        _hb_glyph_info_ligated_and_didnt_multiply (&info[start])))
   {
     unsigned int new_reph_pos;
@@ -1305,7 +1318,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
 	  unlikely (is_halant (info[new_reph_pos])))
       {
 	for (unsigned int i = base + 1; i < new_reph_pos; i++)
-	  if (info[i].indic_category() == OT_M) {
+	  if (info[i].indic_category() == I_Cat(M)) {
 	    /* Ok, got it. */
 	    new_reph_pos--;
 	  }
@@ -1365,7 +1378,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
 	  if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
 	  {
 	    while (new_pos > start &&
-		   !(is_one_of (info[new_pos - 1], FLAG(OT_M) | FLAG (OT_H))))
+		   !(is_one_of (info[new_pos - 1], FLAG(I_Cat(M)) | FLAG (I_Cat(H)))))
 	      new_pos--;
 	  }
 
@@ -1414,11 +1427,10 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
     switch ((hb_tag_t) plan->props.script)
     {
       case HB_SCRIPT_TAMIL:
-      case HB_SCRIPT_SINHALA:
 	break;
 
       default:
-	/* Uniscribe merges the entire syllable into a single cluster... Except for Tamil & Sinhala.
+	/* Uniscribe merges the entire syllable into a single cluster... Except for Tamil.
 	 * This means, half forms are submerged into the main consonant's cluster.
 	 * This is unnecessary, and makes cursor positioning harder, but that's what
 	 * Uniscribe does. */
@@ -1453,7 +1465,9 @@ preprocess_text_indic (const hb_ot_shape_plan_t *plan,
 		       hb_buffer_t              *buffer,
 		       hb_font_t                *font)
 {
-  _hb_preprocess_text_vowel_constraints (plan, buffer, font);
+  const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
+  if (!indic_plan->uniscribe_bug_compatible)
+    _hb_preprocess_text_vowel_constraints (plan, buffer, font);
 }
 
 static bool
@@ -1486,48 +1500,6 @@ decompose_indic (const hb_ot_shape_normalize_context_t *c,
 #endif
   }
 
-  if ((ab == 0x0DDAu || hb_in_range<hb_codepoint_t> (ab, 0x0DDCu, 0x0DDEu)))
-  {
-    /*
-     * Sinhala split matras...  Let the fun begin.
-     *
-     * These four characters have Unicode decompositions.  However, Uniscribe
-     * decomposes them "Khmer-style", that is, it uses the character itself to
-     * get the second half.  The first half of all four decompositions is always
-     * U+0DD9.
-     *
-     * Now, there are buggy fonts, namely, the widely used lklug.ttf, that are
-     * broken with Uniscribe.  But we need to support them.  As such, we only
-     * do the Uniscribe-style decomposition if the character is transformed into
-     * its "sec.half" form by the 'pstf' feature.  Otherwise, we fall back to
-     * Unicode decomposition.
-     *
-     * Note that we can't unconditionally use Unicode decomposition.  That would
-     * break some other fonts, that are designed to work with Uniscribe, and
-     * don't have positioning features for the Unicode-style decomposition.
-     *
-     * Argh...
-     *
-     * The Uniscribe behavior is now documented in the newly published Sinhala
-     * spec in 2012:
-     *
-     *   https://docs.microsoft.com/en-us/typography/script-development/sinhala#shaping
-     */
-
-
-    const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data;
-    hb_codepoint_t glyph;
-    if (indic_plan->uniscribe_bug_compatible ||
-	(c->font->get_nominal_glyph (ab, &glyph) &&
-	 indic_plan->pstf.would_substitute (&glyph, 1, c->font->face)))
-    {
-      /* Ok, safe to use Uniscribe-style decomposition. */
-      *a = 0x0DD9u;
-      *b = ab;
-      return true;
-    }
-  }
-
   return (bool) c->unicode->decompose (ab, a, b);
 }
 
@@ -1548,7 +1520,7 @@ compose_indic (const hb_ot_shape_normalize_context_t *c,
 }
 
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
+const hb_ot_shaper_t _hb_ot_shaper_indic =
 {
   collect_features_indic,
   override_features_indic,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4f822c26e9fe198fb7d5b54ac0409c14b096779a
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-indic.hh
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPER_INDIC_HH
+#define HB_OT_SHAPER_INDIC_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shaper-syllabic.hh"
+
+
+/* Visual positions in a syllable from left to right. */
+enum ot_position_t {
+  POS_START = 0,
+
+  POS_RA_TO_BECOME_REPH = 1,
+  POS_PRE_M = 2,
+  POS_PRE_C = 3,
+
+  POS_BASE_C = 4,
+  POS_AFTER_MAIN = 5,
+
+  POS_ABOVE_C = 6,
+
+  POS_BEFORE_SUB = 7,
+  POS_BELOW_C = 8,
+  POS_AFTER_SUB = 9,
+
+  POS_BEFORE_POST = 10,
+  POS_POST_C = 11,
+  POS_AFTER_POST = 12,
+
+  POS_SMVD = 13,
+
+  POS_END = 14
+};
+
+
+HB_INTERNAL uint16_t
+hb_indic_get_categories (hb_codepoint_t u);
+
+
+#endif /* HB_OT_SHAPER_INDIC_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer-machine.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e18bd75ef1ffcbcaf26dc470357249d949963703
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer-machine.hh
@@ -0,0 +1,428 @@
+
+#line 1 "hb-ot-shaper-khmer-machine.rl"
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPER_KHMER_MACHINE_HH
+#define HB_OT_SHAPER_KHMER_MACHINE_HH
+
+#include "hb.hh"
+
+#include "hb-ot-layout.hh"
+#include "hb-ot-shaper-indic.hh"
+
+/* buffer var allocations */
+#define khmer_category() ot_shaper_var_u8_category() /* khmer_category_t */
+
+using khmer_category_t = unsigned;
+
+#define K_Cat(Cat) khmer_syllable_machine_ex_##Cat
+
+enum khmer_syllable_type_t {
+  khmer_consonant_syllable,
+  khmer_broken_cluster,
+  khmer_non_khmer_cluster,
+};
+
+
+#line 52 "hb-ot-shaper-khmer-machine.hh"
+#define khmer_syllable_machine_ex_C 1u
+#define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u
+#define khmer_syllable_machine_ex_H 4u
+#define khmer_syllable_machine_ex_PLACEHOLDER 10u
+#define khmer_syllable_machine_ex_Ra 15u
+#define khmer_syllable_machine_ex_Robatic 25u
+#define khmer_syllable_machine_ex_V 2u
+#define khmer_syllable_machine_ex_VAbv 20u
+#define khmer_syllable_machine_ex_VBlw 21u
+#define khmer_syllable_machine_ex_VPre 22u
+#define khmer_syllable_machine_ex_VPst 23u
+#define khmer_syllable_machine_ex_Xgroup 26u
+#define khmer_syllable_machine_ex_Ygroup 27u
+#define khmer_syllable_machine_ex_ZWJ 6u
+#define khmer_syllable_machine_ex_ZWNJ 5u
+
+
+#line 70 "hb-ot-shaper-khmer-machine.hh"
+static const unsigned char _khmer_syllable_machine_trans_keys[] = {
+	5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 
+	5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 
+	5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 27u, 4u, 27u, 1u, 15u, 
+	4u, 27u, 4u, 27u, 27u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 
+	4u, 27u, 1u, 15u, 4u, 27u, 4u, 27u, 27u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 
+	4u, 27u, 4u, 27u, 5u, 26u, 0
+};
+
+static const char _khmer_syllable_machine_key_spans[] = {
+	22, 22, 15, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 15, 22, 22, 
+	22, 22, 22, 22, 22, 27, 24, 15, 
+	24, 24, 1, 24, 24, 24, 24, 24, 
+	24, 15, 24, 24, 1, 24, 24, 24, 
+	24, 24, 22
+};
+
+static const short _khmer_syllable_machine_index_offsets[] = {
+	0, 23, 46, 62, 85, 108, 131, 154, 
+	177, 200, 223, 246, 269, 292, 308, 331, 
+	354, 377, 400, 423, 446, 469, 497, 522, 
+	538, 563, 588, 590, 615, 640, 665, 690, 
+	715, 740, 756, 781, 806, 808, 833, 858, 
+	883, 908, 933
+};
+
+static const char _khmer_syllable_machine_indicies[] = {
+	1, 1, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 2, 
+	0, 0, 0, 0, 3, 4, 0, 1, 
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 4, 0, 5, 5, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 5, 0, 1, 1, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 2, 0, 0, 
+	0, 0, 0, 4, 0, 6, 6, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 2, 0, 7, 7, 0, 0, 
+	0, 0, 0, 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, 2, 0, 0, 0, 0, 0, 
+	10, 0, 9, 9, 0, 0, 0, 0, 
+	0, 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, 
+	2, 0, 0, 0, 0, 0, 12, 0, 
+	11, 11, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 12, 0, 1, 
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 2, 0, 
+	0, 0, 0, 13, 4, 0, 15, 15, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 16, 14, 14, 
+	14, 14, 17, 18, 14, 15, 15, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 18, 19, 20, 20, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 20, 14, 15, 15, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 16, 14, 14, 14, 14, 
+	14, 18, 14, 21, 21, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	16, 14, 22, 22, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 23, 
+	14, 24, 24, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	16, 14, 14, 14, 14, 14, 25, 14, 
+	24, 24, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 25, 14, 26, 
+	26, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 16, 14, 
+	14, 14, 14, 14, 27, 14, 26, 26, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 27, 14, 29, 29, 28, 
+	30, 31, 31, 28, 28, 28, 13, 13, 
+	28, 28, 28, 29, 28, 28, 28, 28, 
+	16, 25, 27, 23, 28, 17, 18, 20, 
+	28, 33, 34, 34, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 2, 10, 12, 8, 32, 13, 4, 
+	5, 32, 35, 35, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	35, 32, 33, 36, 36, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 2, 10, 12, 8, 32, 3, 
+	4, 5, 32, 37, 38, 38, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 2, 10, 12, 8, 32, 
+	32, 4, 5, 32, 5, 32, 37, 6, 
+	6, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 8, 32, 32, 2, 5, 32, 37, 
+	7, 7, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 8, 5, 32, 
+	37, 39, 39, 32, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	2, 32, 32, 8, 32, 32, 10, 5, 
+	32, 37, 40, 40, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 2, 10, 32, 8, 32, 32, 12, 
+	5, 32, 33, 38, 38, 32, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 2, 10, 12, 8, 32, 32, 
+	4, 5, 32, 33, 38, 38, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 2, 10, 12, 8, 32, 
+	3, 4, 5, 32, 42, 42, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 42, 41, 30, 43, 43, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 16, 25, 27, 23, 
+	41, 17, 18, 20, 41, 44, 45, 45, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 16, 25, 27, 
+	23, 41, 41, 18, 20, 41, 20, 41, 
+	44, 21, 21, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 23, 41, 41, 16, 20, 
+	41, 44, 22, 22, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 23, 
+	20, 41, 44, 46, 46, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 16, 41, 41, 23, 41, 41, 
+	25, 20, 41, 44, 47, 47, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 16, 25, 41, 23, 41, 
+	41, 27, 20, 41, 30, 45, 45, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 16, 25, 27, 23, 
+	41, 41, 18, 20, 41, 15, 15, 48, 
+	48, 48, 48, 48, 48, 48, 48, 48, 
+	48, 48, 48, 48, 16, 48, 48, 48, 
+	48, 48, 18, 48, 0
+};
+
+static const char _khmer_syllable_machine_trans_targs[] = {
+	21, 1, 27, 31, 25, 26, 4, 5, 
+	28, 7, 29, 9, 30, 32, 21, 12, 
+	37, 41, 35, 21, 36, 15, 16, 38, 
+	18, 39, 20, 40, 21, 22, 33, 42, 
+	21, 23, 10, 24, 0, 2, 3, 6, 
+	8, 21, 34, 11, 13, 14, 17, 19, 
+	21
+};
+
+static const char _khmer_syllable_machine_trans_actions[] = {
+	1, 0, 2, 2, 2, 0, 0, 0, 
+	2, 0, 2, 0, 2, 2, 3, 0, 
+	2, 4, 4, 5, 0, 0, 0, 2, 
+	0, 2, 0, 2, 8, 2, 0, 9, 
+	10, 0, 0, 2, 0, 0, 0, 0, 
+	0, 11, 4, 0, 0, 0, 0, 0, 
+	12
+};
+
+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, 0, 6, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0
+};
+
+static const char _khmer_syllable_machine_from_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 7, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0
+};
+
+static const short _khmer_syllable_machine_eof_trans[] = {
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 15, 20, 15, 15, 15, 
+	15, 15, 15, 15, 15, 0, 33, 33, 
+	33, 33, 33, 33, 33, 33, 33, 33, 
+	33, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 49
+};
+
+static const int khmer_syllable_machine_start = 21;
+static const int khmer_syllable_machine_first_final = 21;
+static const int khmer_syllable_machine_error = -1;
+
+static const int khmer_syllable_machine_en_main = 21;
+
+
+#line 53 "hb-ot-shaper-khmer-machine.rl"
+
+
+
+#line 102 "hb-ot-shaper-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) | syllable_type; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+inline 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 298 "hb-ot-shaper-khmer-machine.hh"
+	{
+	cs = khmer_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 122 "hb-ot-shaper-khmer-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int syllable_serial = 1;
+  
+#line 314 "hb-ot-shaper-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:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 328 "hb-ot-shaper-khmer-machine.hh"
+	}
+
+	_keys = _khmer_syllable_machine_trans_keys + (cs<<1);
+	_inds = _khmer_syllable_machine_indicies + _khmer_syllable_machine_index_offsets[cs];
+
+	_slen = _khmer_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].khmer_category()) &&
+		( info[p].khmer_category()) <= _keys[1] ?
+		( info[p].khmer_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _khmer_syllable_machine_trans_targs[_trans];
+
+	if ( _khmer_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _khmer_syllable_machine_trans_actions[_trans] ) {
+	case 2:
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 8:
+#line 98 "hb-ot-shaper-khmer-machine.rl"
+	{te = p+1;{ found_syllable (khmer_non_khmer_cluster); }}
+	break;
+	case 10:
+#line 96 "hb-ot-shaper-khmer-machine.rl"
+	{te = p;p--;{ found_syllable (khmer_consonant_syllable); }}
+	break;
+	case 11:
+#line 97 "hb-ot-shaper-khmer-machine.rl"
+	{te = p;p--;{ found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 12:
+#line 98 "hb-ot-shaper-khmer-machine.rl"
+	{te = p;p--;{ found_syllable (khmer_non_khmer_cluster); }}
+	break;
+	case 1:
+#line 96 "hb-ot-shaper-khmer-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (khmer_consonant_syllable); }}
+	break;
+	case 3:
+#line 97 "hb-ot-shaper-khmer-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 5:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 2:
+	{{p = ((te))-1;} found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }
+	break;
+	case 3:
+	{{p = ((te))-1;} found_syllable (khmer_non_khmer_cluster); }
+	break;
+	}
+	}
+	break;
+	case 4:
+#line 1 "NONE"
+	{te = p+1;}
+#line 97 "hb-ot-shaper-khmer-machine.rl"
+	{act = 2;}
+	break;
+	case 9:
+#line 1 "NONE"
+	{te = p+1;}
+#line 98 "hb-ot-shaper-khmer-machine.rl"
+	{act = 3;}
+	break;
+#line 398 "hb-ot-shaper-khmer-machine.hh"
+	}
+
+_again:
+	switch ( _khmer_syllable_machine_to_state_actions[cs] ) {
+	case 6:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 407 "hb-ot-shaper-khmer-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _khmer_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _khmer_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 130 "hb-ot-shaper-khmer-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPER_KHMER_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer-machine.rl b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer-machine.rl
similarity index 74%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer-machine.rl
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer-machine.rl
index c9cf33f41da2a6047bbaf1a88f45c8187b01c8ad..f180471bb5a3fe1f826a57e9648b1f32710cb9ac 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer-machine.rl
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer-machine.rl
@@ -24,11 +24,21 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
+#ifndef HB_OT_SHAPER_KHMER_MACHINE_HH
+#define HB_OT_SHAPER_KHMER_MACHINE_HH
 
 #include "hb.hh"
 
+#include "hb-ot-layout.hh"
+#include "hb-ot-shaper-indic.hh"
+
+/* buffer var allocations */
+#define khmer_category() ot_shaper_var_u8_category() /* khmer_category_t */
+
+using khmer_category_t = unsigned;
+
+#define K_Cat(Cat) khmer_syllable_machine_ex_##Cat
+
 enum khmer_syllable_type_t {
   khmer_consonant_syllable,
   khmer_broken_cluster,
@@ -44,21 +54,27 @@ enum khmer_syllable_type_t {
 
 %%{
 
+
+# We use category H for spec category Coeng
+
 export C    = 1;
 export V    = 2;
+export H    = 4;
 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;
+export PLACEHOLDER = 10;
+export DOTTEDCIRCLE = 11;
+export Ra   = 15;
+
+export VAbv = 20;
+export VBlw = 21;
+export VPre = 22;
+export VPst = 23;
+
+export Robatic = 25;
+export Xgroup  = 26;
+export Ygroup  = 27;
+
 
 c = (C | Ra | V);
 cn = c.((ZWJ|ZWNJ)?.Robatic)?;
@@ -69,16 +85,16 @@ ygroup = Ygroup*;
 # This grammar was experimentally extracted from what Uniscribe allows.
 
 matra_group = VPre? xgroup VBlw? xgroup (joiner?.VAbv)? xgroup VPst?;
-syllable_tail = xgroup matra_group xgroup (Coeng.c)? ygroup;
+syllable_tail = xgroup matra_group xgroup (H.c)? ygroup;
 
 
-broken_cluster =	(Coeng.cn)* (Coeng | syllable_tail);
+broken_cluster =	Robatic? (H.cn)* (H | syllable_tail);
 consonant_syllable =	(cn|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster;
 other =			any;
 
 main := |*
 	consonant_syllable	=> { found_syllable (khmer_consonant_syllable); };
-	broken_cluster		=> { found_syllable (khmer_broken_cluster); };
+	broken_cluster		=> { found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; };
 	other			=> { found_syllable (khmer_non_khmer_cluster); };
 *|;
 
@@ -94,7 +110,7 @@ main := |*
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
-static void
+inline void
 find_syllables_khmer (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act HB_UNUSED;
@@ -116,4 +132,4 @@ find_syllables_khmer (hb_buffer_t *buffer)
 
 #undef found_syllable
 
-#endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */
+#endif /* HB_OT_SHAPER_KHMER_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer.cc
similarity index 93%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer.cc
index a7d5bf574b60e2ce3dfaad686e1375a1a3084506..e04d633195876584793983fd2a2a4135f574d1b4 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-khmer.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-khmer.cc
@@ -28,8 +28,8 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex-khmer.hh"
-#include "hb-ot-shape-complex-khmer-machine.hh"
+#include "hb-ot-shaper-khmer-machine.hh"
+#include "hb-ot-shaper-indic.hh"
 #include "hb-ot-layout.hh"
 
 
@@ -37,6 +37,7 @@
  * Khmer shaper.
  */
 
+
 static const hb_ot_map_feature_t
 khmer_features[] =
 {
@@ -79,6 +80,15 @@ enum {
   KHMER_BASIC_FEATURES = _KHMER_PRES, /* Don't forget to update this! */
 };
 
+static inline void
+set_khmer_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+
+  info.khmer_category() = (khmer_category_t) (type & 0xFFu);
+}
+
 static void
 setup_syllables_khmer (const hb_ot_shape_plan_t *plan,
 		       hb_font_t *font,
@@ -115,7 +125,7 @@ collect_features_khmer (hb_ot_shape_planner_t *plan)
     map->add_feature (khmer_features[i]);
 
   /* https://github.com/harfbuzz/harfbuzz/issues/3531 */
-  map->add_gsub_pause (nullptr);
+  map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var
 
   for (; i < KHMER_NUM_FEATURES; i++)
     map->add_feature (khmer_features[i]);
@@ -187,6 +197,7 @@ setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font HB_UNUSED,
 		       hb_buffer_t *buffer)
 {
+  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
   find_syllables_khmer (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
@@ -230,11 +241,11 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan,
      * the 'pref' OpenType feature applied to them.
      * """
      */
-    if (info[i].khmer_category() == OT_Coeng && num_coengs <= 2 && i + 1 < end)
+    if (info[i].khmer_category() == K_Cat(H) && num_coengs <= 2 && i + 1 < end)
     {
       num_coengs++;
 
-      if (info[i + 1].khmer_category() == OT_Ra)
+      if (info[i + 1].khmer_category() == K_Cat(Ra))
       {
 	for (unsigned int j = 0; j < 2; j++)
 	  info[i + j].mask |= khmer_plan->mask_array[KHMER_PREF];
@@ -262,7 +273,7 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan,
     }
 
     /* Reorder left matra piece. */
-    else if (info[i].khmer_category() == OT_VPre)
+    else if (info[i].khmer_category() == K_Cat(VPre))
     {
       /* Move to the start. */
       buffer->merge_clusters (start, i + 1);
@@ -301,8 +312,8 @@ reorder_khmer (const hb_ot_shape_plan_t *plan,
   {
     hb_syllabic_insert_dotted_circles (font, buffer,
 				       khmer_broken_cluster,
-				       OT_DOTTEDCIRCLE,
-				       OT_Repha);
+				       K_Cat(DOTTEDCIRCLE),
+				       (unsigned) -1);
 
     foreach_syllable (buffer, start, end)
       reorder_syllable_khmer (plan, font->face, buffer, start, end);
@@ -349,7 +360,7 @@ compose_khmer (const hb_ot_shape_normalize_context_t *c,
 }
 
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_khmer =
+const hb_ot_shaper_t _hb_ot_shaper_khmer =
 {
   collect_features_khmer,
   override_features_khmer,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar-machine.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b109708937b8eefa5aca7d53c52413a8d02f0f3d
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar-machine.hh
@@ -0,0 +1,553 @@
+
+#line 1 "hb-ot-shaper-myanmar-machine.rl"
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPER_MYANMAR_MACHINE_HH
+#define HB_OT_SHAPER_MYANMAR_MACHINE_HH
+
+#include "hb.hh"
+
+#include "hb-ot-layout.hh"
+#include "hb-ot-shaper-indic.hh"
+
+/* buffer var allocations */
+#define myanmar_category() ot_shaper_var_u8_category() /* myanmar_category_t */
+#define myanmar_position() ot_shaper_var_u8_auxiliary() /* myanmar_position_t */
+
+using myanmar_category_t = unsigned;
+using myanmar_position_t = ot_position_t;
+
+#define M_Cat(Cat) myanmar_syllable_machine_ex_##Cat
+
+enum myanmar_syllable_type_t {
+  myanmar_consonant_syllable,
+  myanmar_broken_cluster,
+  myanmar_non_myanmar_cluster,
+};
+
+
+#line 54 "hb-ot-shaper-myanmar-machine.hh"
+#define myanmar_syllable_machine_ex_A 9u
+#define myanmar_syllable_machine_ex_As 32u
+#define myanmar_syllable_machine_ex_C 1u
+#define myanmar_syllable_machine_ex_CS 18u
+#define myanmar_syllable_machine_ex_DB 3u
+#define myanmar_syllable_machine_ex_DOTTEDCIRCLE 11u
+#define myanmar_syllable_machine_ex_GB 10u
+#define myanmar_syllable_machine_ex_H 4u
+#define myanmar_syllable_machine_ex_IV 2u
+#define myanmar_syllable_machine_ex_MH 35u
+#define myanmar_syllable_machine_ex_ML 41u
+#define myanmar_syllable_machine_ex_MR 36u
+#define myanmar_syllable_machine_ex_MW 37u
+#define myanmar_syllable_machine_ex_MY 38u
+#define myanmar_syllable_machine_ex_PT 39u
+#define myanmar_syllable_machine_ex_Ra 15u
+#define myanmar_syllable_machine_ex_SM 8u
+#define myanmar_syllable_machine_ex_VAbv 20u
+#define myanmar_syllable_machine_ex_VBlw 21u
+#define myanmar_syllable_machine_ex_VPre 22u
+#define myanmar_syllable_machine_ex_VPst 23u
+#define myanmar_syllable_machine_ex_VS 40u
+#define myanmar_syllable_machine_ex_ZWJ 6u
+#define myanmar_syllable_machine_ex_ZWNJ 5u
+
+
+#line 81 "hb-ot-shaper-myanmar-machine.hh"
+static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
+	1u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, 
+	5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 39u, 3u, 39u, 
+	3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 41u, 
+	3u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, 
+	5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 41u, 3u, 39u, 
+	3u, 39u, 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, 
+	3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 1u, 41u, 1u, 15u, 0
+};
+
+static const char _myanmar_syllable_machine_key_spans[] = {
+	41, 39, 35, 4, 39, 37, 37, 35, 
+	35, 37, 37, 39, 35, 15, 37, 37, 
+	38, 37, 39, 39, 37, 39, 39, 39, 
+	39, 39, 35, 4, 39, 37, 37, 35, 
+	35, 37, 37, 39, 35, 15, 39, 37, 
+	37, 38, 37, 39, 39, 37, 39, 39, 
+	39, 39, 39, 39, 39, 41, 15
+};
+
+static const short _myanmar_syllable_machine_index_offsets[] = {
+	0, 42, 82, 118, 123, 163, 201, 239, 
+	275, 311, 349, 387, 427, 463, 479, 517, 
+	555, 594, 632, 672, 712, 750, 790, 830, 
+	870, 910, 950, 986, 991, 1031, 1069, 1107, 
+	1143, 1179, 1217, 1255, 1295, 1331, 1347, 1387, 
+	1425, 1463, 1502, 1540, 1580, 1620, 1658, 1698, 
+	1738, 1778, 1818, 1858, 1898, 1938, 1980
+};
+
+static const char _myanmar_syllable_machine_indicies[] = {
+	1, 1, 2, 3, 4, 4, 0, 5, 
+	6, 1, 1, 0, 0, 0, 7, 0, 
+	0, 8, 0, 9, 10, 11, 12, 0, 
+	0, 0, 0, 0, 0, 0, 0, 13, 
+	0, 0, 14, 15, 16, 17, 18, 19, 
+	20, 0, 22, 23, 24, 24, 21, 25, 
+	26, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 27, 28, 29, 30, 21, 
+	21, 21, 21, 21, 21, 21, 21, 31, 
+	21, 21, 32, 33, 34, 35, 36, 37, 
+	38, 21, 24, 24, 21, 25, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 30, 21, 21, 21, 
+	21, 21, 21, 21, 21, 39, 21, 21, 
+	21, 21, 21, 21, 36, 21, 24, 24, 
+	21, 25, 21, 22, 21, 24, 24, 21, 
+	25, 26, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 40, 21, 21, 30, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	41, 21, 21, 42, 21, 21, 21, 36, 
+	21, 41, 21, 22, 21, 24, 24, 21, 
+	25, 26, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 30, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 36, 
+	21, 43, 21, 24, 24, 21, 25, 36, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 44, 21, 
+	21, 21, 21, 21, 21, 36, 21, 24, 
+	24, 21, 25, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 44, 21, 21, 21, 21, 21, 
+	21, 36, 21, 24, 24, 21, 25, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 36, 21, 22, 
+	21, 24, 24, 21, 25, 26, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	40, 21, 21, 30, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 36, 21, 22, 21, 24, 
+	24, 21, 25, 26, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 40, 21, 
+	21, 30, 21, 21, 21, 21, 21, 21, 
+	21, 21, 41, 21, 21, 21, 21, 21, 
+	21, 36, 21, 22, 21, 24, 24, 21, 
+	25, 26, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 40, 21, 21, 30, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	41, 21, 21, 21, 21, 21, 21, 36, 
+	21, 41, 21, 24, 24, 21, 25, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 30, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 36, 21, 1, 
+	1, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 1, 21, 22, 
+	21, 24, 24, 21, 25, 26, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	27, 28, 21, 30, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 36, 21, 22, 21, 24, 
+	24, 21, 25, 26, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 28, 
+	21, 30, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 36, 21, 22, 21, 24, 24, 21, 
+	25, 26, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 27, 28, 29, 30, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 36, 
+	45, 21, 22, 21, 24, 24, 21, 25, 
+	26, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 27, 28, 29, 30, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 36, 21, 
+	22, 21, 24, 24, 21, 25, 26, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 27, 28, 29, 30, 21, 21, 21, 
+	21, 21, 21, 21, 21, 31, 21, 21, 
+	32, 33, 34, 35, 36, 21, 38, 21, 
+	22, 21, 24, 24, 21, 25, 26, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 27, 28, 29, 30, 21, 21, 21, 
+	21, 21, 21, 21, 21, 45, 21, 21, 
+	21, 21, 21, 21, 36, 21, 38, 21, 
+	22, 21, 24, 24, 21, 25, 26, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 27, 28, 29, 30, 21, 21, 21, 
+	21, 21, 21, 21, 21, 45, 21, 21, 
+	21, 21, 21, 21, 36, 21, 22, 21, 
+	24, 24, 21, 25, 26, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 27, 
+	28, 29, 30, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 32, 21, 
+	34, 21, 36, 21, 38, 21, 22, 21, 
+	24, 24, 21, 25, 26, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 27, 
+	28, 29, 30, 21, 21, 21, 21, 21, 
+	21, 21, 21, 45, 21, 21, 32, 21, 
+	21, 21, 36, 21, 38, 21, 22, 21, 
+	24, 24, 21, 25, 26, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 27, 
+	28, 29, 30, 21, 21, 21, 21, 21, 
+	21, 21, 21, 46, 21, 21, 32, 33, 
+	34, 21, 36, 21, 38, 21, 22, 21, 
+	24, 24, 21, 25, 26, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 27, 
+	28, 29, 30, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 32, 33, 
+	34, 21, 36, 21, 38, 21, 22, 23, 
+	24, 24, 21, 25, 26, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 27, 
+	28, 29, 30, 21, 21, 21, 21, 21, 
+	21, 21, 21, 31, 21, 21, 32, 33, 
+	34, 35, 36, 21, 38, 21, 48, 48, 
+	47, 5, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 49, 47, 47, 47, 47, 47, 47, 
+	18, 47, 48, 48, 47, 5, 47, 2, 
+	47, 48, 48, 47, 5, 6, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	50, 47, 47, 12, 47, 47, 47, 47, 
+	47, 47, 47, 47, 51, 47, 47, 52, 
+	47, 47, 47, 18, 47, 51, 47, 2, 
+	47, 48, 48, 47, 5, 6, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 12, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 18, 47, 53, 47, 48, 
+	48, 47, 5, 18, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 54, 47, 47, 47, 47, 47, 
+	47, 18, 47, 48, 48, 47, 5, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 54, 47, 
+	47, 47, 47, 47, 47, 18, 47, 48, 
+	48, 47, 5, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 18, 47, 2, 47, 48, 48, 47, 
+	5, 6, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 50, 47, 47, 12, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 18, 
+	47, 2, 47, 48, 48, 47, 5, 6, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 50, 47, 47, 12, 47, 47, 
+	47, 47, 47, 47, 47, 47, 51, 47, 
+	47, 47, 47, 47, 47, 18, 47, 2, 
+	47, 48, 48, 47, 5, 6, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	50, 47, 47, 12, 47, 47, 47, 47, 
+	47, 47, 47, 47, 51, 47, 47, 47, 
+	47, 47, 47, 18, 47, 51, 47, 48, 
+	48, 47, 5, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 12, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 18, 47, 55, 55, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 55, 47, 2, 3, 48, 48, 47, 
+	5, 6, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 9, 10, 11, 12, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	13, 47, 47, 14, 15, 16, 17, 18, 
+	19, 20, 47, 2, 47, 48, 48, 47, 
+	5, 6, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 9, 10, 47, 12, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 18, 
+	47, 2, 47, 48, 48, 47, 5, 6, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 10, 47, 12, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 18, 47, 2, 
+	47, 48, 48, 47, 5, 6, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	9, 10, 11, 12, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 18, 56, 47, 2, 47, 
+	48, 48, 47, 5, 6, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 9, 
+	10, 11, 12, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 18, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 9, 10, 11, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 13, 47, 47, 14, 15, 16, 17, 
+	18, 47, 20, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 9, 10, 11, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 56, 47, 47, 47, 47, 47, 47, 
+	18, 47, 20, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 9, 10, 11, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 56, 47, 47, 47, 47, 47, 47, 
+	18, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 14, 47, 16, 47, 18, 47, 
+	20, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 56, 
+	47, 47, 14, 47, 47, 47, 18, 47, 
+	20, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 57, 
+	47, 47, 14, 15, 16, 47, 18, 47, 
+	20, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 14, 15, 16, 47, 18, 47, 
+	20, 47, 2, 3, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 13, 
+	47, 47, 14, 15, 16, 17, 18, 47, 
+	20, 47, 22, 23, 24, 24, 21, 25, 
+	26, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 27, 28, 29, 30, 21, 
+	21, 21, 21, 21, 21, 21, 21, 58, 
+	21, 21, 32, 33, 34, 35, 36, 37, 
+	38, 21, 22, 59, 24, 24, 21, 25, 
+	26, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 27, 28, 29, 30, 21, 
+	21, 21, 21, 21, 21, 21, 21, 31, 
+	21, 21, 32, 33, 34, 35, 36, 21, 
+	38, 21, 1, 1, 2, 3, 48, 48, 
+	47, 5, 6, 1, 1, 47, 47, 47, 
+	1, 47, 47, 47, 47, 9, 10, 11, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 13, 47, 47, 14, 15, 16, 17, 
+	18, 19, 20, 47, 1, 1, 60, 60, 
+	60, 60, 60, 60, 60, 1, 1, 60, 
+	60, 60, 1, 60, 0
+};
+
+static const char _myanmar_syllable_machine_trans_targs[] = {
+	0, 1, 26, 37, 0, 27, 29, 51, 
+	54, 39, 40, 41, 28, 43, 44, 46, 
+	47, 48, 30, 50, 45, 0, 2, 13, 
+	0, 3, 5, 14, 15, 16, 4, 18, 
+	19, 21, 22, 23, 6, 25, 20, 12, 
+	9, 10, 11, 7, 8, 17, 24, 0, 
+	0, 36, 33, 34, 35, 31, 32, 38, 
+	42, 49, 52, 53, 0
+};
+
+static const char _myanmar_syllable_machine_trans_actions[] = {
+	3, 0, 0, 0, 4, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 5, 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, 7, 
+	8, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 9
+};
+
+static const char _myanmar_syllable_machine_to_state_actions[] = {
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0
+};
+
+static const char _myanmar_syllable_machine_from_state_actions[] = {
+	2, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0
+};
+
+static const short _myanmar_syllable_machine_eof_trans[] = {
+	0, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 48, 48, 48, 48, 48, 48, 
+	48, 48, 48, 48, 48, 48, 48, 48, 
+	48, 48, 48, 48, 48, 48, 48, 48, 
+	48, 48, 48, 22, 22, 48, 61
+};
+
+static const int myanmar_syllable_machine_start = 0;
+static const int myanmar_syllable_machine_first_final = 0;
+static const int myanmar_syllable_machine_error = -1;
+
+static const int myanmar_syllable_machine_en_main = 0;
+
+
+#line 55 "hb-ot-shaper-myanmar-machine.rl"
+
+
+
+#line 117 "hb-ot-shaper-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) | syllable_type; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+inline void
+find_syllables_myanmar (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts, te, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 447 "hb-ot-shaper-myanmar-machine.hh"
+	{
+	cs = myanmar_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 137 "hb-ot-shaper-myanmar-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int syllable_serial = 1;
+  
+#line 463 "hb-ot-shaper-myanmar-machine.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _myanmar_syllable_machine_from_state_actions[cs] ) {
+	case 2:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 477 "hb-ot-shaper-myanmar-machine.hh"
+	}
+
+	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
+	_inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs];
+
+	_slen = _myanmar_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) &&
+		( info[p].myanmar_category()) <= _keys[1] ?
+		( info[p].myanmar_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _myanmar_syllable_machine_trans_targs[_trans];
+
+	if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
+	case 6:
+#line 110 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_consonant_syllable); }}
+	break;
+	case 4:
+#line 111 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
+	break;
+	case 8:
+#line 112 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 3:
+#line 113 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
+	break;
+	case 5:
+#line 110 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_consonant_syllable); }}
+	break;
+	case 7:
+#line 112 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 9:
+#line 113 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }}
+	break;
+#line 523 "hb-ot-shaper-myanmar-machine.hh"
+	}
+
+_again:
+	switch ( _myanmar_syllable_machine_to_state_actions[cs] ) {
+	case 1:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 532 "hb-ot-shaper-myanmar-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _myanmar_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 145 "hb-ot-shaper-myanmar-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPER_MYANMAR_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar-machine.rl b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar-machine.rl
similarity index 65%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar-machine.rl
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar-machine.rl
index 2e6ac783f6aa4330ea045bf0cc21d34f083e0ac7..2cb6e4e7720357e380153e68409e50ca307383c5 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar-machine.rl
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar-machine.rl
@@ -24,14 +24,25 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+#ifndef HB_OT_SHAPER_MYANMAR_MACHINE_HH
+#define HB_OT_SHAPER_MYANMAR_MACHINE_HH
 
 #include "hb.hh"
 
+#include "hb-ot-layout.hh"
+#include "hb-ot-shaper-indic.hh"
+
+/* buffer var allocations */
+#define myanmar_category() ot_shaper_var_u8_category() /* myanmar_category_t */
+#define myanmar_position() ot_shaper_var_u8_auxiliary() /* myanmar_position_t */
+
+using myanmar_category_t = unsigned;
+using myanmar_position_t = ot_position_t;
+
+#define M_Cat(Cat) myanmar_syllable_machine_ex_##Cat
+
 enum myanmar_syllable_type_t {
   myanmar_consonant_syllable,
-  myanmar_punctuation_cluster,
   myanmar_broken_cluster,
   myanmar_non_myanmar_cluster,
 };
@@ -45,32 +56,38 @@ enum myanmar_syllable_type_t {
 
 %%{
 
-export A    = 10;
-export As   = 18;
+
+# Spec category D is folded into GB; D0 is not implemented by Uniscribe and as such folded into D
+# Spec category P is folded into GB
+
 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 ML   = 33;
-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 DB   = 3;	# Dot below	     = OT_N
+export H    = 4;
 export ZWNJ = 5;
-export Ra   = 16;
-export P    = 31;
-export CS   = 19;
+export ZWJ  = 6;
+export SM    = 8;	# Visarga and Shan tones
+export GB   = 10;	# 		     = OT_PLACEHOLDER
+export DOTTEDCIRCLE = 11;
+export A    = 9;
+export Ra   = 15;
+export CS   = 18;
+
+export VAbv = 20;
+export VBlw = 21;
+export VPre = 22;
+export VPst = 23;
+
+# 32+ are for Myanmar-specific values
+export As   = 32;	# Asat
+export MH   = 35;	# Medial Ha
+export MR   = 36;	# Medial Ra
+export MW   = 37;	# Medial Wa, Shan Wa
+export MY   = 38;	# Medial Ya, Mon Na, Mon Ma
+export PT   = 39;	# Pwo and other tones
+export VS   = 40;	# Variation selectors
+export ML   = 41;	# Medial Mon La
+
 
 j = ZWJ|ZWNJ;			# Joiners
 k = (Ra As H);			# Kinzi
@@ -82,19 +99,17 @@ main_vowel_group = (VPre.VS?)* VAbv* VBlw* A* (DB As?)?;
 post_vowel_group = VPst MH? ML? As* VAbv* A* (DB As?)?;
 pwo_tone_group = PT A* DB? As?;
 
-complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* V* j?;
+complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* SM* j?;
 syllable_tail = (H (c|IV).VS?)* (H | complex_syllable_tail);
 
-consonant_syllable =	(k|CS)? (c|IV|D|GB).VS? syllable_tail;
-punctuation_cluster =	P V;
+consonant_syllable =	(k|CS)? (c|IV|GB|DOTTEDCIRCLE).VS? syllable_tail;
 broken_cluster =	k? VS? syllable_tail;
 other =			any;
 
 main := |*
 	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); };
+	broken_cluster		=> { found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; };
 	other			=> { found_syllable (myanmar_non_myanmar_cluster); };
 *|;
 
@@ -110,7 +125,7 @@ main := |*
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
-static void
+inline void
 find_syllables_myanmar (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act HB_UNUSED;
@@ -132,4 +147,4 @@ find_syllables_myanmar (hb_buffer_t *buffer)
 
 #undef found_syllable
 
-#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */
+#endif /* HB_OT_SHAPER_MYANMAR_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar.cc
similarity index 77%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar.cc
index 13beaf4d4ce640b3fd15a855c10891446525ef52..1ccafbca7e79651dcf8fa0fb80356b931c259ac7 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-myanmar.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-myanmar.cc
@@ -28,14 +28,16 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex-myanmar.hh"
-#include "hb-ot-shape-complex-myanmar-machine.hh"
+#include "hb-ot-shaper-myanmar-machine.hh"
+#include "hb-ot-shaper-indic.hh"
+#include "hb-ot-layout.hh"
 
 
 /*
  * Myanmar shaper.
  */
 
+
 static const hb_tag_t
 myanmar_basic_features[] =
 {
@@ -62,6 +64,40 @@ myanmar_other_features[] =
   HB_TAG('p','s','t','s'),
 };
 
+static inline void
+set_myanmar_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+
+  info.myanmar_category() = (myanmar_category_t) (type & 0xFFu);
+}
+
+
+static inline bool
+is_one_of_myanmar (const hb_glyph_info_t &info, unsigned int flags)
+{
+  /* If it ligated, all bets are off. */
+  if (_hb_glyph_info_ligated (&info)) return false;
+  return !!(FLAG_UNSAFE (info.myanmar_category()) & flags);
+}
+
+/* Note:
+ *
+ * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
+ * cannot happen in a consonant syllable.  The plus side however is, we can call the
+ * consonant syllable logic from the vowel syllable function and get it all right!
+ *
+ * Keep in sync with consonant_categories in the generator. */
+#define CONSONANT_FLAGS_MYANMAR (FLAG (M_Cat(C)) | FLAG (M_Cat(CS)) | FLAG (M_Cat(Ra)) | /* FLAG (M_Cat(CM)) | */ FLAG (M_Cat(IV)) | FLAG (M_Cat(GB)) | FLAG (M_Cat(DOTTEDCIRCLE)))
+
+static inline bool
+is_consonant_myanmar (const hb_glyph_info_t &info)
+{
+  return is_one_of_myanmar (info, CONSONANT_FLAGS_MYANMAR);
+}
+
+
 static void
 setup_syllables_myanmar (const hb_ot_shape_plan_t *plan,
 			 hb_font_t *font,
@@ -92,6 +128,7 @@ collect_features_myanmar (hb_ot_shape_planner_t *plan)
     map->enable_feature (myanmar_basic_features[i], F_MANUAL_ZWJ | F_PER_SYLLABLE);
     map->add_gsub_pause (nullptr);
   }
+  map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++)
     map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ);
@@ -118,6 +155,7 @@ setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
 			 hb_font_t *font HB_UNUSED,
 			 hb_buffer_t *buffer)
 {
+  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
   find_syllables_myanmar (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
@@ -129,7 +167,7 @@ compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
   int a = pa->myanmar_position();
   int b = pb->myanmar_position();
 
-  return a < b ? -1 : a == b ? 0 : +1;
+  return (int) a - (int) b;
 }
 
 
@@ -148,9 +186,9 @@ initial_reordering_consonant_syllable (hb_buffer_t *buffer,
   {
     unsigned int limit = start;
     if (start + 3 <= end &&
-	info[start  ].myanmar_category() == OT_Ra &&
-	info[start+1].myanmar_category() == OT_As &&
-	info[start+2].myanmar_category() == OT_H)
+	info[start  ].myanmar_category() == M_Cat(Ra) &&
+	info[start+1].myanmar_category() == M_Cat(As) &&
+	info[start+2].myanmar_category() == M_Cat(H))
     {
       limit += 3;
       base = start;
@@ -162,7 +200,7 @@ initial_reordering_consonant_syllable (hb_buffer_t *buffer,
 	base = limit;
 
       for (unsigned int i = limit; i < end; i++)
-	if (is_consonant (info[i]))
+	if (is_consonant_myanmar (info[i]))
 	{
 	  base = i;
 	  break;
@@ -187,39 +225,40 @@ initial_reordering_consonant_syllable (hb_buffer_t *buffer,
      * Myanmar reordering! */
     for (; i < end; i++)
     {
-      if (info[i].myanmar_category() == OT_MR) /* Pre-base reordering */
+      if (info[i].myanmar_category() == M_Cat(MR)) /* Pre-base reordering */
       {
 	info[i].myanmar_position() = POS_PRE_C;
 	continue;
       }
-      if (info[i].myanmar_position() < POS_BASE_C) /* Left matra */
+      if (info[i].myanmar_category() == M_Cat(VPre)) /* Left matra */
       {
+	info[i].myanmar_position() = POS_PRE_M;
 	continue;
       }
-      if (info[i].myanmar_category() == OT_VS)
+      if (info[i].myanmar_category() == M_Cat(VS))
       {
 	info[i].myanmar_position() = info[i - 1].myanmar_position();
 	continue;
       }
 
-      if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == OT_VBlw)
+      if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == M_Cat(VBlw))
       {
 	pos = POS_BELOW_C;
 	info[i].myanmar_position() = pos;
 	continue;
       }
 
-      if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_A)
+      if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(A))
       {
 	info[i].myanmar_position() = POS_BEFORE_SUB;
 	continue;
       }
-      if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_VBlw)
+      if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(VBlw))
       {
 	info[i].myanmar_position() = pos;
 	continue;
       }
-      if (pos == POS_BELOW_C && info[i].myanmar_category() != OT_A)
+      if (pos == POS_BELOW_C && info[i].myanmar_category() != M_Cat(A))
       {
 	pos = POS_AFTER_SUB;
 	info[i].myanmar_position() = pos;
@@ -247,7 +286,6 @@ reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
       initial_reordering_consonant_syllable  (buffer, start, end);
       break;
 
-    case myanmar_punctuation_cluster:
     case myanmar_non_myanmar_cluster:
       break;
   }
@@ -262,7 +300,7 @@ reorder_myanmar (const hb_ot_shape_plan_t *plan,
   {
     hb_syllabic_insert_dotted_circles (font, buffer,
 				       myanmar_broken_cluster,
-				       OT_GB);
+				       M_Cat(DOTTEDCIRCLE));
 
     foreach_syllable (buffer, start, end)
       reorder_syllable_myanmar (plan, font->face, buffer, start, end);
@@ -274,7 +312,7 @@ reorder_myanmar (const hb_ot_shape_plan_t *plan,
 }
 
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
+const hb_ot_shaper_t _hb_ot_shaper_myanmar =
 {
   collect_features_myanmar,
   nullptr, /* override_features */
@@ -296,7 +334,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
 /* Ugly Zawgyi encoding.
  * Disable all auto processing.
  * https://github.com/harfbuzz/harfbuzz/issues/1162 */
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_zawgyi =
+const hb_ot_shaper_t _hb_ot_shaper_myanmar_zawgyi =
 {
   nullptr, /* collect_features */
   nullptr, /* override_features */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-syllabic.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-syllabic.cc
similarity index 78%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-syllabic.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-syllabic.cc
index 76092c7f38e01e3686bb33e7ff8c3ad982a41d8f..a8e0d8e8c1e49ac1804aa83fb51d818af598fe61 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-syllabic.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-syllabic.cc
@@ -26,7 +26,7 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex-syllabic.hh"
+#include "hb-ot-shaper-syllabic.hh"
 
 
 void
@@ -39,31 +39,18 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font,
 {
   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))
+  if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE)))
     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.ot_shaper_var_u8_category() = dottedcircle_category;
   if (dottedcircle_position != -1)
-    dottedcircle.complex_var_u8_auxiliary() = dottedcircle_position;
+    dottedcircle.ot_shaper_var_u8_auxiliary() = dottedcircle_position;
   dottedcircle.codepoint = dottedcircle_glyph;
 
   buffer->clear_output ();
@@ -87,7 +74,7 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font,
       {
 	while (buffer->idx < buffer->len && buffer->successful &&
 	       last_syllable == buffer->cur().syllable() &&
-	       buffer->cur().complex_var_u8_category() == (unsigned) repha_category)
+	       buffer->cur().ot_shaper_var_u8_category() == (unsigned) repha_category)
 	  (void) buffer->next_glyph ();
       }
 
@@ -99,5 +86,13 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font,
   buffer->sync ();
 }
 
+HB_INTERNAL void
+hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
+{
+  HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
+}
+
 
 #endif
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-syllabic.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-syllabic.hh
similarity index 83%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-syllabic.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-syllabic.hh
index b901a660d364ae6c22dafc260c598f8c11d08aaf..e8a15bb48ac7ea6303b13417cae9f7cd708f8634 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-syllabic.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-syllabic.hh
@@ -22,12 +22,12 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_SYLLABIC_HH
-#define HB_OT_SHAPE_COMPLEX_SYLLABIC_HH
+#ifndef HB_OT_SHAPER_SYLLABIC_HH
+#define HB_OT_SHAPER_SYLLABIC_HH
 
 #include "hb.hh"
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 
 
 HB_INTERNAL void
@@ -38,5 +38,10 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font,
 				   int repha_category = -1,
 				   int dottedcircle_position = -1);
 
+HB_INTERNAL void
+hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer);
+
 
-#endif /* HB_OT_SHAPE_COMPLEX_SYLLABIC_HH */
+#endif /* HB_OT_SHAPER_SYLLABIC_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-thai.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-thai.cc
similarity index 96%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-thai.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-thai.cc
index a1e27a83be9d7b95b9fa2a40376e84baa452cf87..1280e7ed17ba858a4dd505197e28701a9ef48ccb 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-thai.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-thai.cc
@@ -28,7 +28,7 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 
 
 /* Thai / Lao shaper */
@@ -222,7 +222,7 @@ do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		     hb_buffer_t              *buffer,
 		     hb_font_t                *font)
 {
-#ifdef HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK
+#ifdef HB_NO_OT_SHAPER_THAI_FALLBACK
   return;
 #endif
 
@@ -279,7 +279,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
    * to be what Uniscribe and other engines implement.  According to Eric Muller:
    *
    * When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the
-   * NIKHAHIT backwards over any tone mark (0E48-0E4B).
+   * NIKHAHIT backwards over any above-base marks.
    *
    * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
    *
@@ -308,8 +308,8 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
    * Nikhahit:		U+0E4D	U+0ECD
    *
    * Testing shows that Uniscribe reorder the following marks:
-   * Thai:	<0E31,0E34..0E37,0E47..0E4E>
-   * Lao:	<0EB1,0EB4..0EB7,0EC7..0ECE>
+   * Thai:	<0E31,0E34..0E37,     0E47..0E4E>
+   * Lao:	<0EB1,0EB4..0EB7,0EBB,0EC8..0ECD>
    *
    * Note how the Lao versions are the same as Thai + 0x80.
    */
@@ -319,7 +319,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
 #define IS_SARA_AM(x) (((x) & ~0x0080u) == 0x0E33u)
 #define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0x0E33u + 0x0E4Du)
 #define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
-#define IS_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u))
+#define IS_ABOVE_BASE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u, 0x0E3Bu, 0x0E3Bu))
 
   buffer->clear_output ();
   unsigned int count = buffer->len;
@@ -343,7 +343,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
 
     /* Ok, let's see... */
     unsigned int start = end - 2;
-    while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint))
+    while (start > 0 && IS_ABOVE_BASE_MARK (buffer->out_info[start - 1].codepoint))
       start--;
 
     if (start + 2 < end)
@@ -371,7 +371,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
     do_thai_pua_shaping (plan, buffer, font);
 }
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
+const hb_ot_shaper_t _hb_ot_shaper_thai =
 {
   nullptr, /* collect_features */
   nullptr, /* override_features */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-machine.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-machine.hh
new file mode 100644
index 0000000000000000000000000000000000000000..65e65ff39d00a6389b3c90fe367405eb8fe5de51
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-machine.hh
@@ -0,0 +1,931 @@
+
+#line 1 "hb-ot-shaper-use-machine.rl"
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPER_USE_MACHINE_HH
+#define HB_OT_SHAPER_USE_MACHINE_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shaper-syllabic.hh"
+
+/* buffer var allocations */
+#define use_category() ot_shaper_var_u8_category()
+
+#define USE(Cat) use_syllable_machine_ex_##Cat
+
+enum use_syllable_type_t {
+  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,
+};
+
+
+#line 57 "hb-ot-shaper-use-machine.hh"
+#define use_syllable_machine_ex_B 1u
+#define use_syllable_machine_ex_CGJ 6u
+#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 53u
+#define use_syllable_machine_ex_IS 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_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_WJ 16u
+#define use_syllable_machine_ex_ZWNJ 14u
+
+
+#line 101 "hb-ot-shaper-use-machine.hh"
+static const unsigned char _use_syllable_machine_trans_keys[] = {
+	0u, 53u, 11u, 53u, 11u, 53u, 1u, 53u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, 
+	45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, 22u, 53u, 
+	23u, 53u, 23u, 53u, 23u, 53u, 12u, 53u, 23u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 
+	11u, 53u, 1u, 1u, 1u, 48u, 11u, 53u, 41u, 42u, 42u, 42u, 11u, 53u, 11u, 53u, 
+	1u, 53u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 
+	24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, 22u, 53u, 23u, 53u, 23u, 53u, 23u, 53u, 
+	12u, 53u, 23u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, 1u, 1u, 1u, 48u, 
+	13u, 13u, 4u, 4u, 11u, 53u, 11u, 53u, 1u, 53u, 23u, 48u, 24u, 47u, 25u, 47u, 
+	26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, 
+	22u, 53u, 23u, 53u, 23u, 53u, 23u, 53u, 12u, 53u, 23u, 53u, 12u, 53u, 12u, 53u, 
+	12u, 53u, 11u, 53u, 1u, 1u, 1u, 48u, 11u, 53u, 11u, 53u, 1u, 53u, 23u, 48u, 
+	24u, 47u, 25u, 47u, 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 
+	1u, 1u, 24u, 48u, 22u, 53u, 23u, 53u, 23u, 53u, 23u, 53u, 12u, 53u, 23u, 53u, 
+	12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, 1u, 1u, 1u, 48u, 4u, 4u, 13u, 13u, 
+	1u, 53u, 11u, 53u, 41u, 42u, 42u, 42u, 1u, 5u, 50u, 52u, 49u, 52u, 49u, 51u, 
+	0
+};
+
+static const char _use_syllable_machine_key_spans[] = {
+	54, 43, 43, 53, 26, 24, 23, 22, 
+	2, 1, 25, 25, 25, 1, 25, 32, 
+	31, 31, 31, 42, 31, 42, 42, 42, 
+	43, 1, 48, 43, 2, 1, 43, 43, 
+	53, 26, 24, 23, 22, 2, 1, 25, 
+	25, 25, 1, 25, 32, 31, 31, 31, 
+	42, 31, 42, 42, 42, 43, 1, 48, 
+	1, 1, 43, 43, 53, 26, 24, 23, 
+	22, 2, 1, 25, 25, 25, 1, 25, 
+	32, 31, 31, 31, 42, 31, 42, 42, 
+	42, 43, 1, 48, 43, 43, 53, 26, 
+	24, 23, 22, 2, 1, 25, 25, 25, 
+	1, 25, 32, 31, 31, 31, 42, 31, 
+	42, 42, 42, 43, 1, 48, 1, 1, 
+	53, 43, 2, 1, 5, 3, 4, 3
+};
+
+static const short _use_syllable_machine_index_offsets[] = {
+	0, 55, 99, 143, 197, 224, 249, 273, 
+	296, 299, 301, 327, 353, 379, 381, 407, 
+	440, 472, 504, 536, 579, 611, 654, 697, 
+	740, 784, 786, 835, 879, 882, 884, 928, 
+	972, 1026, 1053, 1078, 1102, 1125, 1128, 1130, 
+	1156, 1182, 1208, 1210, 1236, 1269, 1301, 1333, 
+	1365, 1408, 1440, 1483, 1526, 1569, 1613, 1615, 
+	1664, 1666, 1668, 1712, 1756, 1810, 1837, 1862, 
+	1886, 1909, 1912, 1914, 1940, 1966, 1992, 1994, 
+	2020, 2053, 2085, 2117, 2149, 2192, 2224, 2267, 
+	2310, 2353, 2397, 2399, 2448, 2492, 2536, 2590, 
+	2617, 2642, 2666, 2689, 2692, 2694, 2720, 2746, 
+	2772, 2774, 2800, 2833, 2865, 2897, 2929, 2972, 
+	3004, 3047, 3090, 3133, 3177, 3179, 3228, 3230, 
+	3232, 3286, 3330, 3333, 3335, 3341, 3345, 3350
+};
+
+static const unsigned char _use_syllable_machine_indicies[] = {
+	0, 1, 2, 2, 3, 4, 2, 2, 
+	2, 2, 2, 5, 6, 7, 2, 2, 
+	2, 2, 8, 2, 2, 2, 9, 10, 
+	11, 12, 13, 14, 15, 16, 17, 18, 
+	19, 20, 21, 22, 2, 23, 24, 25, 
+	2, 26, 27, 28, 29, 30, 31, 32, 
+	29, 33, 2, 34, 2, 35, 2, 37, 
+	38, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 39, 40, 41, 42, 43, 44, 
+	45, 46, 47, 48, 49, 50, 51, 52, 
+	36, 53, 54, 55, 36, 56, 57, 36, 
+	58, 59, 60, 61, 58, 36, 36, 36, 
+	36, 62, 36, 37, 38, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 39, 40, 
+	41, 42, 43, 44, 45, 46, 47, 49, 
+	49, 50, 51, 52, 36, 53, 54, 55, 
+	36, 36, 36, 36, 58, 59, 60, 61, 
+	58, 36, 36, 36, 36, 62, 36, 37, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 40, 41, 42, 
+	43, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 53, 54, 55, 36, 36, 
+	36, 36, 36, 59, 60, 61, 63, 36, 
+	36, 36, 36, 40, 36, 40, 41, 42, 
+	43, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 53, 54, 55, 36, 36, 
+	36, 36, 36, 59, 60, 61, 63, 36, 
+	41, 42, 43, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 59, 60, 61, 
+	36, 42, 43, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 59, 60, 61, 
+	36, 43, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 59, 60, 61, 36, 
+	59, 60, 36, 60, 36, 41, 42, 43, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 53, 54, 55, 36, 36, 36, 
+	36, 36, 59, 60, 61, 63, 36, 41, 
+	42, 43, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 54, 55, 36, 
+	36, 36, 36, 36, 59, 60, 61, 63, 
+	36, 41, 42, 43, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	55, 36, 36, 36, 36, 36, 59, 60, 
+	61, 63, 36, 64, 36, 41, 42, 43, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 59, 60, 61, 63, 36, 39, 
+	40, 41, 42, 43, 36, 36, 36, 36, 
+	36, 36, 50, 51, 52, 36, 53, 54, 
+	55, 36, 36, 36, 36, 36, 59, 60, 
+	61, 63, 36, 36, 36, 36, 40, 36, 
+	40, 41, 42, 43, 36, 36, 36, 36, 
+	36, 36, 50, 51, 52, 36, 53, 54, 
+	55, 36, 36, 36, 36, 36, 59, 60, 
+	61, 63, 36, 36, 36, 36, 40, 36, 
+	40, 41, 42, 43, 36, 36, 36, 36, 
+	36, 36, 36, 51, 52, 36, 53, 54, 
+	55, 36, 36, 36, 36, 36, 59, 60, 
+	61, 63, 36, 36, 36, 36, 40, 36, 
+	40, 41, 42, 43, 36, 36, 36, 36, 
+	36, 36, 36, 36, 52, 36, 53, 54, 
+	55, 36, 36, 36, 36, 36, 59, 60, 
+	61, 63, 36, 36, 36, 36, 40, 36, 
+	65, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 39, 40, 41, 42, 43, 36, 
+	45, 46, 36, 36, 36, 50, 51, 52, 
+	36, 53, 54, 55, 36, 36, 36, 36, 
+	36, 59, 60, 61, 63, 36, 36, 36, 
+	36, 40, 36, 40, 41, 42, 43, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 53, 54, 55, 36, 36, 36, 36, 
+	36, 59, 60, 61, 63, 36, 36, 36, 
+	36, 40, 36, 65, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 39, 40, 41, 
+	42, 43, 36, 36, 46, 36, 36, 36, 
+	50, 51, 52, 36, 53, 54, 55, 36, 
+	36, 36, 36, 36, 59, 60, 61, 63, 
+	36, 36, 36, 36, 40, 36, 65, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	39, 40, 41, 42, 43, 36, 36, 36, 
+	36, 36, 36, 50, 51, 52, 36, 53, 
+	54, 55, 36, 36, 36, 36, 36, 59, 
+	60, 61, 63, 36, 36, 36, 36, 40, 
+	36, 65, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 39, 40, 41, 42, 43, 
+	44, 45, 46, 36, 36, 36, 50, 51, 
+	52, 36, 53, 54, 55, 36, 36, 36, 
+	36, 36, 59, 60, 61, 63, 36, 36, 
+	36, 36, 40, 36, 37, 38, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 39, 
+	40, 41, 42, 43, 44, 45, 46, 47, 
+	36, 49, 50, 51, 52, 36, 53, 54, 
+	55, 36, 36, 36, 36, 58, 59, 60, 
+	61, 58, 36, 36, 36, 36, 62, 36, 
+	37, 36, 37, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 36, 36, 
+	40, 41, 42, 43, 36, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 53, 54, 
+	55, 36, 36, 36, 36, 36, 59, 60, 
+	61, 63, 36, 37, 38, 36, 36, 36, 
+	36, 36, 36, 36, 36, 36, 39, 40, 
+	41, 42, 43, 44, 45, 46, 47, 48, 
+	49, 50, 51, 52, 36, 53, 54, 55, 
+	36, 36, 36, 36, 58, 59, 60, 61, 
+	58, 36, 36, 36, 36, 62, 36, 56, 
+	57, 36, 57, 36, 67, 68, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 69, 
+	70, 71, 72, 73, 74, 75, 76, 77, 
+	1, 78, 79, 80, 81, 66, 82, 83, 
+	84, 66, 66, 66, 66, 85, 86, 87, 
+	88, 89, 66, 66, 66, 66, 90, 66, 
+	67, 68, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 69, 70, 71, 72, 73, 
+	74, 75, 76, 77, 78, 78, 79, 80, 
+	81, 66, 82, 83, 84, 66, 66, 66, 
+	66, 85, 86, 87, 88, 89, 66, 66, 
+	66, 66, 90, 66, 67, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 70, 71, 72, 73, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	82, 83, 84, 66, 66, 66, 66, 66, 
+	86, 87, 88, 91, 66, 66, 66, 66, 
+	70, 66, 70, 71, 72, 73, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	82, 83, 84, 66, 66, 66, 66, 66, 
+	86, 87, 88, 91, 66, 71, 72, 73, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 86, 87, 88, 66, 72, 73, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 86, 87, 88, 66, 73, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 86, 87, 88, 66, 86, 87, 66, 
+	87, 66, 71, 72, 73, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 82, 
+	83, 84, 66, 66, 66, 66, 66, 86, 
+	87, 88, 91, 66, 71, 72, 73, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 83, 84, 66, 66, 66, 66, 
+	66, 86, 87, 88, 91, 66, 71, 72, 
+	73, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 84, 66, 66, 
+	66, 66, 66, 86, 87, 88, 91, 66, 
+	93, 92, 71, 72, 73, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 86, 
+	87, 88, 91, 66, 69, 70, 71, 72, 
+	73, 66, 66, 66, 66, 66, 66, 79, 
+	80, 81, 66, 82, 83, 84, 66, 66, 
+	66, 66, 66, 86, 87, 88, 91, 66, 
+	66, 66, 66, 70, 66, 70, 71, 72, 
+	73, 66, 66, 66, 66, 66, 66, 79, 
+	80, 81, 66, 82, 83, 84, 66, 66, 
+	66, 66, 66, 86, 87, 88, 91, 66, 
+	66, 66, 66, 70, 66, 70, 71, 72, 
+	73, 66, 66, 66, 66, 66, 66, 66, 
+	80, 81, 66, 82, 83, 84, 66, 66, 
+	66, 66, 66, 86, 87, 88, 91, 66, 
+	66, 66, 66, 70, 66, 70, 71, 72, 
+	73, 66, 66, 66, 66, 66, 66, 66, 
+	66, 81, 66, 82, 83, 84, 66, 66, 
+	66, 66, 66, 86, 87, 88, 91, 66, 
+	66, 66, 66, 70, 66, 94, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 69, 
+	70, 71, 72, 73, 66, 75, 76, 66, 
+	66, 66, 79, 80, 81, 66, 82, 83, 
+	84, 66, 66, 66, 66, 66, 86, 87, 
+	88, 91, 66, 66, 66, 66, 70, 66, 
+	70, 71, 72, 73, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 82, 83, 
+	84, 66, 66, 66, 66, 66, 86, 87, 
+	88, 91, 66, 66, 66, 66, 70, 66, 
+	94, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 69, 70, 71, 72, 73, 66, 
+	66, 76, 66, 66, 66, 79, 80, 81, 
+	66, 82, 83, 84, 66, 66, 66, 66, 
+	66, 86, 87, 88, 91, 66, 66, 66, 
+	66, 70, 66, 94, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 69, 70, 71, 
+	72, 73, 66, 66, 66, 66, 66, 66, 
+	79, 80, 81, 66, 82, 83, 84, 66, 
+	66, 66, 66, 66, 86, 87, 88, 91, 
+	66, 66, 66, 66, 70, 66, 94, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	69, 70, 71, 72, 73, 74, 75, 76, 
+	66, 66, 66, 79, 80, 81, 66, 82, 
+	83, 84, 66, 66, 66, 66, 66, 86, 
+	87, 88, 91, 66, 66, 66, 66, 70, 
+	66, 67, 68, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 69, 70, 71, 72, 
+	73, 74, 75, 76, 77, 66, 78, 79, 
+	80, 81, 66, 82, 83, 84, 66, 66, 
+	66, 66, 85, 86, 87, 88, 89, 66, 
+	66, 66, 66, 90, 66, 67, 95, 67, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 70, 71, 72, 
+	73, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 82, 83, 84, 66, 66, 
+	66, 66, 66, 86, 87, 88, 91, 66, 
+	97, 96, 3, 98, 99, 100, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 101, 
+	102, 103, 104, 105, 106, 107, 108, 109, 
+	110, 111, 112, 113, 114, 66, 115, 116, 
+	117, 66, 56, 57, 66, 118, 119, 120, 
+	88, 121, 66, 66, 66, 66, 122, 66, 
+	99, 100, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 101, 102, 103, 104, 105, 
+	106, 107, 108, 109, 111, 111, 112, 113, 
+	114, 66, 115, 116, 117, 66, 66, 66, 
+	66, 118, 119, 120, 88, 121, 66, 66, 
+	66, 66, 122, 66, 99, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 102, 103, 104, 105, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	115, 116, 117, 66, 66, 66, 66, 66, 
+	119, 120, 88, 123, 66, 66, 66, 66, 
+	102, 66, 102, 103, 104, 105, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	115, 116, 117, 66, 66, 66, 66, 66, 
+	119, 120, 88, 123, 66, 103, 104, 105, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 119, 120, 88, 66, 104, 105, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 119, 120, 88, 66, 105, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 119, 120, 88, 66, 119, 120, 66, 
+	120, 66, 103, 104, 105, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 115, 
+	116, 117, 66, 66, 66, 66, 66, 119, 
+	120, 88, 123, 66, 103, 104, 105, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 116, 117, 66, 66, 66, 66, 
+	66, 119, 120, 88, 123, 66, 103, 104, 
+	105, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 117, 66, 66, 
+	66, 66, 66, 119, 120, 88, 123, 66, 
+	124, 92, 103, 104, 105, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 119, 
+	120, 88, 123, 66, 101, 102, 103, 104, 
+	105, 66, 66, 66, 66, 66, 66, 112, 
+	113, 114, 66, 115, 116, 117, 66, 66, 
+	66, 66, 66, 119, 120, 88, 123, 66, 
+	66, 66, 66, 102, 66, 102, 103, 104, 
+	105, 66, 66, 66, 66, 66, 66, 112, 
+	113, 114, 66, 115, 116, 117, 66, 66, 
+	66, 66, 66, 119, 120, 88, 123, 66, 
+	66, 66, 66, 102, 66, 102, 103, 104, 
+	105, 66, 66, 66, 66, 66, 66, 66, 
+	113, 114, 66, 115, 116, 117, 66, 66, 
+	66, 66, 66, 119, 120, 88, 123, 66, 
+	66, 66, 66, 102, 66, 102, 103, 104, 
+	105, 66, 66, 66, 66, 66, 66, 66, 
+	66, 114, 66, 115, 116, 117, 66, 66, 
+	66, 66, 66, 119, 120, 88, 123, 66, 
+	66, 66, 66, 102, 66, 125, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 101, 
+	102, 103, 104, 105, 66, 107, 108, 66, 
+	66, 66, 112, 113, 114, 66, 115, 116, 
+	117, 66, 66, 66, 66, 66, 119, 120, 
+	88, 123, 66, 66, 66, 66, 102, 66, 
+	102, 103, 104, 105, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 115, 116, 
+	117, 66, 66, 66, 66, 66, 119, 120, 
+	88, 123, 66, 66, 66, 66, 102, 66, 
+	125, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 101, 102, 103, 104, 105, 66, 
+	66, 108, 66, 66, 66, 112, 113, 114, 
+	66, 115, 116, 117, 66, 66, 66, 66, 
+	66, 119, 120, 88, 123, 66, 66, 66, 
+	66, 102, 66, 125, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 101, 102, 103, 
+	104, 105, 66, 66, 66, 66, 66, 66, 
+	112, 113, 114, 66, 115, 116, 117, 66, 
+	66, 66, 66, 66, 119, 120, 88, 123, 
+	66, 66, 66, 66, 102, 66, 125, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	101, 102, 103, 104, 105, 106, 107, 108, 
+	66, 66, 66, 112, 113, 114, 66, 115, 
+	116, 117, 66, 66, 66, 66, 66, 119, 
+	120, 88, 123, 66, 66, 66, 66, 102, 
+	66, 99, 100, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 101, 102, 103, 104, 
+	105, 106, 107, 108, 109, 66, 111, 112, 
+	113, 114, 66, 115, 116, 117, 66, 66, 
+	66, 66, 118, 119, 120, 88, 121, 66, 
+	66, 66, 66, 122, 66, 99, 95, 99, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 66, 66, 102, 103, 104, 
+	105, 66, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 115, 116, 117, 66, 66, 
+	66, 66, 66, 119, 120, 88, 123, 66, 
+	99, 100, 66, 66, 66, 66, 66, 66, 
+	66, 66, 66, 101, 102, 103, 104, 105, 
+	106, 107, 108, 109, 110, 111, 112, 113, 
+	114, 66, 115, 116, 117, 66, 66, 66, 
+	66, 118, 119, 120, 88, 121, 66, 66, 
+	66, 66, 122, 66, 5, 6, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 9, 
+	10, 11, 12, 13, 14, 15, 16, 17, 
+	19, 19, 20, 21, 22, 126, 23, 24, 
+	25, 126, 126, 126, 126, 29, 30, 31, 
+	32, 29, 126, 126, 126, 126, 35, 126, 
+	5, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 10, 11, 
+	12, 13, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 23, 24, 25, 126, 
+	126, 126, 126, 126, 30, 31, 32, 127, 
+	126, 126, 126, 126, 10, 126, 10, 11, 
+	12, 13, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 23, 24, 25, 126, 
+	126, 126, 126, 126, 30, 31, 32, 127, 
+	126, 11, 12, 13, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 30, 31, 
+	32, 126, 12, 13, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 30, 31, 
+	32, 126, 13, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 30, 31, 32, 
+	126, 30, 31, 126, 31, 126, 11, 12, 
+	13, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 23, 24, 25, 126, 126, 
+	126, 126, 126, 30, 31, 32, 127, 126, 
+	11, 12, 13, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 24, 25, 
+	126, 126, 126, 126, 126, 30, 31, 32, 
+	127, 126, 11, 12, 13, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 25, 126, 126, 126, 126, 126, 30, 
+	31, 32, 127, 126, 128, 126, 11, 12, 
+	13, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 30, 31, 32, 127, 126, 
+	9, 10, 11, 12, 13, 126, 126, 126, 
+	126, 126, 126, 20, 21, 22, 126, 23, 
+	24, 25, 126, 126, 126, 126, 126, 30, 
+	31, 32, 127, 126, 126, 126, 126, 10, 
+	126, 10, 11, 12, 13, 126, 126, 126, 
+	126, 126, 126, 20, 21, 22, 126, 23, 
+	24, 25, 126, 126, 126, 126, 126, 30, 
+	31, 32, 127, 126, 126, 126, 126, 10, 
+	126, 10, 11, 12, 13, 126, 126, 126, 
+	126, 126, 126, 126, 21, 22, 126, 23, 
+	24, 25, 126, 126, 126, 126, 126, 30, 
+	31, 32, 127, 126, 126, 126, 126, 10, 
+	126, 10, 11, 12, 13, 126, 126, 126, 
+	126, 126, 126, 126, 126, 22, 126, 23, 
+	24, 25, 126, 126, 126, 126, 126, 30, 
+	31, 32, 127, 126, 126, 126, 126, 10, 
+	126, 129, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 9, 10, 11, 12, 13, 
+	126, 15, 16, 126, 126, 126, 20, 21, 
+	22, 126, 23, 24, 25, 126, 126, 126, 
+	126, 126, 30, 31, 32, 127, 126, 126, 
+	126, 126, 10, 126, 10, 11, 12, 13, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 23, 24, 25, 126, 126, 126, 
+	126, 126, 30, 31, 32, 127, 126, 126, 
+	126, 126, 10, 126, 129, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 9, 10, 
+	11, 12, 13, 126, 126, 16, 126, 126, 
+	126, 20, 21, 22, 126, 23, 24, 25, 
+	126, 126, 126, 126, 126, 30, 31, 32, 
+	127, 126, 126, 126, 126, 10, 126, 129, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 9, 10, 11, 12, 13, 126, 126, 
+	126, 126, 126, 126, 20, 21, 22, 126, 
+	23, 24, 25, 126, 126, 126, 126, 126, 
+	30, 31, 32, 127, 126, 126, 126, 126, 
+	10, 126, 129, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 9, 10, 11, 12, 
+	13, 14, 15, 16, 126, 126, 126, 20, 
+	21, 22, 126, 23, 24, 25, 126, 126, 
+	126, 126, 126, 30, 31, 32, 127, 126, 
+	126, 126, 126, 10, 126, 5, 6, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	9, 10, 11, 12, 13, 14, 15, 16, 
+	17, 126, 19, 20, 21, 22, 126, 23, 
+	24, 25, 126, 126, 126, 126, 29, 30, 
+	31, 32, 29, 126, 126, 126, 126, 35, 
+	126, 5, 126, 5, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 10, 11, 12, 13, 126, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 23, 
+	24, 25, 126, 126, 126, 126, 126, 30, 
+	31, 32, 127, 126, 130, 126, 7, 126, 
+	1, 126, 126, 126, 1, 126, 126, 126, 
+	126, 126, 5, 6, 7, 126, 126, 126, 
+	126, 126, 126, 126, 126, 9, 10, 11, 
+	12, 13, 14, 15, 16, 17, 18, 19, 
+	20, 21, 22, 126, 23, 24, 25, 126, 
+	26, 27, 126, 29, 30, 31, 32, 29, 
+	126, 126, 126, 126, 35, 126, 5, 6, 
+	126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 9, 10, 11, 12, 13, 14, 15, 
+	16, 17, 18, 19, 20, 21, 22, 126, 
+	23, 24, 25, 126, 126, 126, 126, 29, 
+	30, 31, 32, 29, 126, 126, 126, 126, 
+	35, 126, 26, 27, 126, 27, 126, 1, 
+	131, 131, 131, 1, 131, 133, 132, 33, 
+	132, 33, 133, 132, 133, 132, 33, 132, 
+	34, 132, 0
+};
+
+static const char _use_syllable_machine_trans_targs[] = {
+	1, 30, 0, 56, 58, 85, 86, 110, 
+	112, 98, 87, 88, 89, 90, 102, 104, 
+	105, 106, 113, 107, 99, 100, 101, 93, 
+	94, 95, 114, 115, 116, 108, 91, 92, 
+	0, 117, 119, 109, 0, 2, 3, 15, 
+	4, 5, 6, 7, 19, 21, 22, 23, 
+	27, 24, 16, 17, 18, 10, 11, 12, 
+	28, 29, 25, 8, 9, 0, 26, 13, 
+	14, 20, 0, 31, 32, 44, 33, 34, 
+	35, 36, 48, 50, 51, 52, 53, 45, 
+	46, 47, 39, 40, 41, 54, 37, 38, 
+	0, 54, 55, 42, 0, 43, 49, 0, 
+	0, 57, 0, 59, 60, 72, 61, 62, 
+	63, 64, 76, 78, 79, 80, 84, 81, 
+	73, 74, 75, 67, 68, 69, 82, 65, 
+	66, 82, 83, 70, 71, 77, 0, 96, 
+	97, 103, 111, 0, 0, 118
+};
+
+static const char _use_syllable_machine_trans_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, 
+	4, 0, 0, 0, 5, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 6, 0, 0, 
+	0, 0, 7, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 8, 0, 0, 
+	9, 10, 0, 0, 11, 0, 0, 12, 
+	13, 0, 14, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 8, 0, 
+	0, 10, 0, 0, 0, 0, 15, 0, 
+	0, 0, 0, 16, 17, 0
+};
+
+static const char _use_syllable_machine_to_state_actions[] = {
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const char _use_syllable_machine_from_state_actions[] = {
+	2, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	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[] = {
+	0, 37, 37, 37, 37, 37, 37, 37, 
+	37, 37, 37, 37, 37, 37, 37, 37, 
+	37, 37, 37, 37, 37, 37, 37, 37, 
+	37, 37, 37, 37, 37, 37, 67, 67, 
+	67, 67, 67, 67, 67, 67, 67, 67, 
+	67, 67, 93, 67, 67, 67, 67, 67, 
+	67, 67, 67, 67, 67, 67, 96, 67, 
+	97, 99, 67, 67, 67, 67, 67, 67, 
+	67, 67, 67, 67, 67, 67, 93, 67, 
+	67, 67, 67, 67, 67, 67, 67, 67, 
+	67, 67, 96, 67, 67, 127, 127, 127, 
+	127, 127, 127, 127, 127, 127, 127, 127, 
+	127, 127, 127, 127, 127, 127, 127, 127, 
+	127, 127, 127, 127, 127, 127, 127, 127, 
+	127, 127, 127, 127, 132, 133, 133, 133
+};
+
+static const int use_syllable_machine_start = 0;
+static const int use_syllable_machine_first_final = 0;
+static const int use_syllable_machine_error = -1;
+
+static const int use_syllable_machine_en_main = 0;
+
+
+#line 58 "hb-ot-shaper-use-machine.rl"
+
+
+
+#line 182 "hb-ot-shaper-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) | 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) : hb_iter_with_fallback_t<machine_index_t<Iter>,
+								       typename Iter::item_t> (),
+					       it (o.it), is_null (o.is_null) {}
+
+  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)
+  {
+    assert (n == 0);
+    is_null = true;
+  }
+  explicit operator bool () { return !is_null; }
+
+  void operator = (const machine_index_t& o)
+  {
+    is_null = o.is_null;
+    unsigned index = (*it).first;
+    unsigned n = (*o.it).first;
+    if (index < n) it += n - index; else if (index > n) it -= index - n;
+  }
+  bool operator == (const machine_index_t& o) const
+  { return is_null ? o.is_null : !o.is_null && (*it).first == (*o.it).first; }
+  bool operator != (const machine_index_t& o) const { return !(*this == o); }
+
+  private:
+  Iter it;
+  bool is_null = false;
+};
+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_ccs_default_ignorable (const hb_glyph_info_t &i)
+{ return i.use_category() != USE(CGJ); }
+
+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_ccs_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_ccs_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 784 "hb-ot-shaper-use-machine.hh"
+	{
+	cs = use_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 282 "hb-ot-shaper-use-machine.rl"
+
+
+  unsigned int syllable_serial = 1;
+  
+#line 797 "hb-ot-shaper-use-machine.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const unsigned char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _use_syllable_machine_from_state_actions[cs] ) {
+	case 2:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 811 "hb-ot-shaper-use-machine.hh"
+	}
+
+	_keys = _use_syllable_machine_trans_keys + (cs<<1);
+	_inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs];
+
+	_slen = _use_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( (*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 9:
+#line 172 "hb-ot-shaper-use-machine.rl"
+	{te = p+1;{ found_syllable (use_standard_cluster); }}
+	break;
+	case 6:
+#line 175 "hb-ot-shaper-use-machine.rl"
+	{te = p+1;{ found_syllable (use_symbol_cluster); }}
+	break;
+	case 4:
+#line 177 "hb-ot-shaper-use-machine.rl"
+	{te = p+1;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 3:
+#line 178 "hb-ot-shaper-use-machine.rl"
+	{te = p+1;{ found_syllable (use_non_cluster); }}
+	break;
+	case 11:
+#line 171 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_sakot_terminated_cluster); }}
+	break;
+	case 7:
+#line 172 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_standard_cluster); }}
+	break;
+	case 14:
+#line 173 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_number_joiner_terminated_cluster); }}
+	break;
+	case 13:
+#line 174 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_numeral_cluster); }}
+	break;
+	case 5:
+#line 175 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_symbol_cluster); }}
+	break;
+	case 17:
+#line 176 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_hieroglyph_cluster); }}
+	break;
+	case 15:
+#line 177 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 16:
+#line 178 "hb-ot-shaper-use-machine.rl"
+	{te = p;p--;{ found_syllable (use_non_cluster); }}
+	break;
+	case 12:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 1:
+	{{p = ((te))-1;} found_syllable (use_virama_terminated_cluster); }
+	break;
+	case 2:
+	{{p = ((te))-1;} found_syllable (use_sakot_terminated_cluster); }
+	break;
+	}
+	}
+	break;
+	case 8:
+#line 1 "NONE"
+	{te = p+1;}
+#line 170 "hb-ot-shaper-use-machine.rl"
+	{act = 1;}
+	break;
+	case 10:
+#line 1 "NONE"
+	{te = p+1;}
+#line 171 "hb-ot-shaper-use-machine.rl"
+	{act = 2;}
+	break;
+#line 901 "hb-ot-shaper-use-machine.hh"
+	}
+
+_again:
+	switch ( _use_syllable_machine_to_state_actions[cs] ) {
+	case 1:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 910 "hb-ot-shaper-use-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _use_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 287 "hb-ot-shaper-use-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPER_USE_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-machine.rl b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-machine.rl
similarity index 94%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-machine.rl
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-machine.rl
index 3f2d5bfba71641d791885e538f2733e449a71b4b..4bb378452733bd965142c9f86d45e0d7ebbb3a15 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-machine.rl
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-machine.rl
@@ -26,15 +26,15 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+#ifndef HB_OT_SHAPER_USE_MACHINE_HH
+#define HB_OT_SHAPER_USE_MACHINE_HH
 
 #include "hb.hh"
 
-#include "hb-ot-shape-complex-syllabic.hh"
+#include "hb-ot-shaper-syllabic.hh"
 
 /* buffer var allocations */
-#define use_category() complex_var_u8_category()
+#define use_category() ot_shaper_var_u8_category()
 
 #define USE(Cat) use_syllable_machine_ex_##Cat
 
@@ -82,6 +82,7 @@ export G	= 49; # HIEROGLYPH
 export J	= 50; # HIEROGLYPH_JOINER
 export SB	= 51; # HIEROGLYPH_SEGMENT_BEGIN
 export SE	= 52; # HIEROGLYPH_SEGMENT_END
+export HVM	= 53; # HALANT_OR_VOWEL_MODIFIER
 
 export FAbv	= 24; # CONS_FINAL_ABOVE
 export FBlw	= 25; # CONS_FINAL_BELOW
@@ -107,12 +108,12 @@ export FMBlw	= 46; # CONS_FINAL_MOD	UIPC = Bottom
 export FMPst	= 47; # CONS_FINAL_MOD	UIPC = Not_Applicable
 
 
-h = H | IS | Sk;
+h = H | HVM | IS | Sk;
 
 consonant_modifiers = CMAbv* CMBlw* ((h B | SUB) CMAbv? CMBlw*)*;
 medial_consonants = MPre? MAbv? MBlw? MPst?;
 dependent_vowels = VPre* VAbv* VBlw* VPst* | H;
-vowel_modifiers = VMPre* VMAbv* VMBlw* VMPst*;
+vowel_modifiers = HVM? VMPre* VMAbv* VMBlw* VMPst*;
 final_consonants = FAbv* FBlw* FPst*;
 final_modifiers = FMAbv* FMBlw* | FMPst?;
 
@@ -173,7 +174,7 @@ main := |*
 	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); };
+	broken_cluster				=> { found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; };
 	other					=> { found_syllable (use_non_cluster); };
 *|;
 
@@ -288,4 +289,4 @@ find_syllables_use (hb_buffer_t *buffer)
 
 #undef found_syllable
 
-#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
+#endif /* HB_OT_SHAPER_USE_MACHINE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-table.hh
similarity index 99%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-table.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-table.hh
index ea627cd27ef7038c96ad0a74883e4619eb76665d..5aa025fb4c6cc72e1df172ef55ff8e73b6d7ec55 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use-table.hh
@@ -37,12 +37,12 @@
  * UnicodeData.txt does not have a header.
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_USE_TABLE_HH
-#define HB_OT_SHAPE_COMPLEX_USE_TABLE_HH
+#ifndef HB_OT_SHAPER_USE_TABLE_HH
+#define HB_OT_SHAPER_USE_TABLE_HH
 
 #include "hb.hh"
 
-#include "hb-ot-shape-complex-use-machine.hh"
+#include "hb-ot-shaper-use-machine.hh"
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-macros"
@@ -53,6 +53,7 @@
 #define GB	USE(GB)	/* BASE_OTHER */
 #define H	USE(H)	/* HALANT */
 #define HN	USE(HN)	/* HALANT_NUM */
+#define HVM	USE(HVM)	/* HALANT_OR_VOWEL_MODIFIER */
 #define IS	USE(IS)	/* INVISIBLE_STACKER */
 #define J	USE(J)	/* HIEROGLYPH_JOINER */
 #define N	USE(N)	/* BASE_NUM */
@@ -246,7 +247,7 @@ static const uint8_t use_table[] = {
   /* 0D90 */     B,     B,     B,     B,     B,     B,     B,    WJ,    WJ,    WJ,     B,     B,     B,     B,     B,     B,
   /* 0DA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0DB0 */     B,     B,    WJ,     B,     B,     B,     B,     B,     B,     B,     B,     B,    WJ,     B,    WJ,    WJ,
-  /* 0DC0 */     B,     B,     B,     B,     B,     B,     B,    WJ,    WJ,    WJ,     H,    WJ,    WJ,    WJ,    WJ,  VPst,
+  /* 0DC0 */     B,     B,     B,     B,     B,     B,     B,    WJ,    WJ,    WJ,   HVM,    WJ,    WJ,    WJ,    WJ,  VPst,
   /* 0DD0 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,    WJ,  VBlw,    WJ,  VPst,  VPre,  VPre,  VPre,  VPre,  VPre,  VPre,  VPst,
   /* 0DE0 */    WJ,    WJ,    WJ,    WJ,    WJ,    WJ,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0DF0 */    WJ,    WJ,  VPst,  VPst,     O,    WJ,    WJ,    WJ,
@@ -606,7 +607,7 @@ static const uint8_t use_table[] = {
   /* 10A00 */     B,  VBlw,  VBlw,  VBlw,    WJ,  VAbv,  VBlw,    WJ,    WJ,    WJ,    WJ,    WJ,  VPst, VMBlw, VMBlw, VMAbv,
   /* 10A10 */     B,     B,     B,     B,    WJ,     B,     B,     B,    WJ,     B,     B,     B,     B,     B,     B,     B,
   /* 10A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 10A30 */     B,     B,     B,     B,     B,     B,    WJ,    WJ, CMAbv, CMBlw, CMBlw,    WJ,    WJ,    WJ,    WJ,    IS,
+  /* 10A30 */     B,     B,     B,     B,     B,     B,    WJ,    WJ, CMBlw, CMBlw, CMBlw,    WJ,    WJ,    WJ,    WJ,    IS,
   /* 10A40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,    WJ,    WJ,    WJ,    WJ,    WJ,    WJ,    WJ,
 
 #define use_offset_0x10ac0u 4304
@@ -1531,6 +1532,7 @@ hb_use_get_category (hb_glyph_info_t info)
 #undef GB
 #undef H
 #undef HN
+#undef HVM
 #undef IS
 #undef J
 #undef N
@@ -1566,5 +1568,5 @@ hb_use_get_category (hb_glyph_info_t info)
 #undef VMPre
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_USE_TABLE_HH */
+#endif /* HB_OT_SHAPER_USE_TABLE_HH */
 /* == End of generated table == */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use.cc
similarity index 96%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use.cc
index 1d13c8a126b059e0c062e463532f350dd18b7091..60394ed4d8a34601046960d889ff66676d6ddfe9 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-use.cc
@@ -30,11 +30,11 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#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"
+#include "hb-ot-shaper-use-machine.hh"
+#include "hb-ot-shaper-use-table.hh"
+#include "hb-ot-shaper-arabic.hh"
+#include "hb-ot-shaper-arabic-joining-list.hh"
+#include "hb-ot-shaper-vowel-constraints.hh"
 
 
 /*
@@ -133,6 +133,7 @@ collect_features_use (hb_ot_shape_planner_t *plan)
     map->enable_feature (use_basic_features[i], F_MANUAL_ZWJ | F_PER_SYLLABLE);
 
   map->add_gsub_pause (reorder_use);
+  map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var
 
   /* "Topographical features" */
   for (unsigned int i = 0; i < ARRAY_LENGTH (use_topographical_features); i++)
@@ -297,6 +298,7 @@ setup_syllables_use (const hb_ot_shape_plan_t *plan,
 		     hb_font_t *font HB_UNUSED,
 		     hb_buffer_t *buffer)
 {
+  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
   find_syllables_use (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
@@ -349,7 +351,7 @@ record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED,
 static inline bool
 is_halant_use (const hb_glyph_info_t &info)
 {
-  return (info.use_category() == USE(H) || info.use_category() == USE(IS)) &&
+  return (info.use_category() == USE(H) || info.use_category() == USE(HVM) || info.use_category() == USE(IS)) &&
 	 !_hb_glyph_info_ligated (&info);
 }
 
@@ -480,7 +482,7 @@ compose_use (const hb_ot_shape_normalize_context_t *c,
 }
 
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use =
+const hb_ot_shaper_t _hb_ot_shaper_use =
 {
   collect_features_use,
   nullptr, /* override_features */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-vowel-constraints.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.cc
similarity index 99%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-vowel-constraints.cc
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.cc
index d2cca105a4951a8def4c79cb59e29db883346c56..be4ac813b13e92196fdcc3372dbf5a8e9ec64bf6 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-vowel-constraints.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.cc
@@ -18,7 +18,7 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex-vowel-constraints.hh"
+#include "hb-ot-shaper-vowel-constraints.hh"
 
 static void
 _output_dotted_circle (hb_buffer_t *buffer)
@@ -39,7 +39,7 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,
 				       hb_buffer_t              *buffer,
 				       hb_font_t                *font HB_UNUSED)
 {
-#ifdef HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS
+#ifdef HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS
   return;
 #endif
   if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-vowel-constraints.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.hh
similarity index 87%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-vowel-constraints.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.hh
index d9082d4eadbdd7c54b1254525e3e3eb2a7a2c835..5a7ee1b0f27271bc55b582ccdc1f2e0d49361aa9 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-vowel-constraints.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.hh
@@ -24,16 +24,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
-#define HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
+#ifndef HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH
+#define HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH
 
 #include "hb.hh"
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shaper.hh"
 
 HB_INTERNAL void
 _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,
 				       hb_buffer_t              *buffer,
 				       hb_font_t                *font);
 
-#endif /* HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH */
+#endif /* HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper.hh
similarity index 84%
rename from source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex.hh
rename to source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper.hh
index 8012a9ae9465f472b920787e4981316938b1d841..b823bf003c5c7e0ce25424df3197ad0121e8c24e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper.hh
@@ -24,8 +24,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_HH
-#define HB_OT_SHAPE_COMPLEX_HH
+#ifndef HB_OT_SHAPER_HH
+#define HB_OT_SHAPER_HH
 
 #include "hb.hh"
 
@@ -34,12 +34,12 @@
 #include "hb-ot-shape-normalize.hh"
 
 
-/* buffer var allocations, used by complex shapers */
-#define complex_var_u8_category()	var2.u8[2]
-#define complex_var_u8_auxiliary()	var2.u8[3]
+/* buffer var allocations, used by all OT shapers */
+#define ot_shaper_var_u8_category()	var2.u8[2]
+#define ot_shaper_var_u8_auxiliary()	var2.u8[3]
 
 
-#define HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS 32
+#define HB_OT_SHAPE_MAX_COMBINING_MARKS 32
 
 enum hb_ot_shape_zero_width_marks_type_t {
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
@@ -49,22 +49,22 @@ enum hb_ot_shape_zero_width_marks_type_t {
 
 
 /* Master OT shaper list */
-#define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
-  HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (default) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (dumber) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (khmer) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (use) \
+#define HB_OT_SHAPERS_IMPLEMENT_SHAPERS \
+  HB_OT_SHAPER_IMPLEMENT (arabic) \
+  HB_OT_SHAPER_IMPLEMENT (default) \
+  HB_OT_SHAPER_IMPLEMENT (dumber) \
+  HB_OT_SHAPER_IMPLEMENT (hangul) \
+  HB_OT_SHAPER_IMPLEMENT (hebrew) \
+  HB_OT_SHAPER_IMPLEMENT (indic) \
+  HB_OT_SHAPER_IMPLEMENT (khmer) \
+  HB_OT_SHAPER_IMPLEMENT (myanmar) \
+  HB_OT_SHAPER_IMPLEMENT (myanmar_zawgyi) \
+  HB_OT_SHAPER_IMPLEMENT (thai) \
+  HB_OT_SHAPER_IMPLEMENT (use) \
   /* ^--- Add new shapers here; keep sorted. */
 
 
-struct hb_ot_complex_shaper_t
+struct hb_ot_shaper_t
 {
   /* collect_features()
    * Called during shape_plan().
@@ -168,18 +168,18 @@ struct hb_ot_complex_shaper_t
   bool fallback_position;
 };
 
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_complex_shaper_t _hb_ot_complex_shaper_##name;
-HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
+#define HB_OT_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_shaper_t _hb_ot_shaper_##name;
+HB_OT_SHAPERS_IMPLEMENT_SHAPERS
+#undef HB_OT_SHAPER_IMPLEMENT
 
 
-static inline const hb_ot_complex_shaper_t *
-hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
+static inline const hb_ot_shaper_t *
+hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner)
 {
   switch ((hb_tag_t) planner->props.script)
   {
     default:
-      return &_hb_ot_complex_shaper_default;
+      return &_hb_ot_shaper_default;
 
 
     /* Unicode-1.1 additions */
@@ -195,28 +195,28 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
       if ((planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT ||
 	   planner->props.script == HB_SCRIPT_ARABIC) &&
 	  HB_DIRECTION_IS_HORIZONTAL(planner->props.direction))
-	return &_hb_ot_complex_shaper_arabic;
+	return &_hb_ot_shaper_arabic;
       else
-	return &_hb_ot_complex_shaper_default;
+	return &_hb_ot_shaper_default;
 
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_THAI:
     case HB_SCRIPT_LAO:
 
-      return &_hb_ot_complex_shaper_thai;
+      return &_hb_ot_shaper_thai;
 
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_HANGUL:
 
-      return &_hb_ot_complex_shaper_hangul;
+      return &_hb_ot_shaper_hangul;
 
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_HEBREW:
 
-      return &_hb_ot_complex_shaper_hebrew;
+      return &_hb_ot_shaper_hebrew;
 
 
     /* Unicode-1.1 additions */
@@ -230,9 +230,6 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
     case HB_SCRIPT_TAMIL:
     case HB_SCRIPT_TELUGU:
 
-    /* Unicode-3.0 additions */
-    case HB_SCRIPT_SINHALA:
-
       /* If the designer designed the font for the 'DFLT' script,
        * (or we ended up arbitrarily pick 'latn'), use the default shaper.
        * Otherwise, use the specific shaper.
@@ -240,14 +237,14 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
        * If it's indy3 tag, send to USE. */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
 	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n'))
-	return &_hb_ot_complex_shaper_default;
+	return &_hb_ot_shaper_default;
       else if ((planner->map.chosen_script[0] & 0x000000FF) == '3')
-	return &_hb_ot_complex_shaper_use;
+	return &_hb_ot_shaper_use;
       else
-	return &_hb_ot_complex_shaper_indic;
+	return &_hb_ot_shaper_indic;
 
     case HB_SCRIPT_KHMER:
-	return &_hb_ot_complex_shaper_khmer;
+	return &_hb_ot_shaper_khmer;
 
     case HB_SCRIPT_MYANMAR:
       /* If the designer designed the font for the 'DFLT' script,
@@ -260,16 +257,16 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
 	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n') ||
 	  planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
-	return &_hb_ot_complex_shaper_default;
+	return &_hb_ot_shaper_default;
       else
-	return &_hb_ot_complex_shaper_myanmar;
+	return &_hb_ot_shaper_myanmar;
 
 
 #define HB_SCRIPT_MYANMAR_ZAWGYI	((hb_script_t) HB_TAG ('Q','a','a','g'))
     case HB_SCRIPT_MYANMAR_ZAWGYI:
     /* https://github.com/harfbuzz/harfbuzz/issues/1162 */
 
-      return &_hb_ot_complex_shaper_myanmar_zawgyi;
+      return &_hb_ot_shaper_myanmar_zawgyi;
 
 
     /* Unicode-2.0 additions */
@@ -277,7 +274,7 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
 
     /* Unicode-3.0 additions */
     case HB_SCRIPT_MONGOLIAN:
-    //case HB_SCRIPT_SINHALA:
+    case HB_SCRIPT_SINHALA:
 
     /* Unicode-3.2 additions */
     case HB_SCRIPT_BUHID:
@@ -390,11 +387,11 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
        * GSUB/GPOS needed, so there may be no scripts found! */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
 	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n'))
-	return &_hb_ot_complex_shaper_default;
+	return &_hb_ot_shaper_default;
       else
-	return &_hb_ot_complex_shaper_use;
+	return &_hb_ot_shaper_use;
   }
 }
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_HH */
+#endif /* HB_OT_SHAPER_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh
index 526178327742bdae5b9b4ddbdc31eb1f9c93fd12..9394b90ee61d6978c9eba70e69d9c0e790fbad7b 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh
@@ -13,1612 +13,1617 @@
 #ifndef HB_OT_TAG_TABLE_HH
 #define HB_OT_TAG_TABLE_HH
 
-static const LangTag ot_languages[] = {
-  {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
-  {"aae",	HB_TAG('S','Q','I',' ')},	/* Arbëreshë Albanian -> Albanian */
-  {"aao",	HB_TAG('A','R','A',' ')},	/* Algerian Saharan Arabic -> Arabic */
-  {"aat",	HB_TAG('S','Q','I',' ')},	/* Arvanitika Albanian -> Albanian */
-  {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
-  {"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('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 */
-  {"ada",	HB_TAG('D','N','G',' ')},	/* Adangme -> Dangme */
-  {"adf",	HB_TAG('A','R','A',' ')},	/* Dhofari Arabic -> Arabic */
-  {"adp",	HB_TAG('D','Z','N',' ')},	/* Adap (retired code) -> Dzongkha */
-/*{"ady",	HB_TAG('A','D','Y',' ')},*/	/* Adyghe */
-  {"aeb",	HB_TAG('A','R','A',' ')},	/* Tunisian Arabic -> Arabic */
-  {"aec",	HB_TAG('A','R','A',' ')},	/* Saidi Arabic -> Arabic */
-  {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
-  {"afb",	HB_TAG('A','R','A',' ')},	/* Gulf Arabic -> Arabic */
-  {"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 */
-  {"ajt",	HB_TAG('A','R','A',' ')},	/* Judeo-Tunisian Arabic (retired code) -> Arabic */
-  {"ak",	HB_TAG('A','K','A',' ')},	/* Akan [macrolanguage] */
-  {"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 */
-  {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
-  {"amf",	HB_TAG('H','B','N',' ')},	/* Hamer-Banna -> Hammer-Banna */
-  {"amw",	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic -> Syriac */
-  {"an",	HB_TAG('A','R','G',' ')},	/* Aragonese */
-/*{"ang",	HB_TAG('A','N','G',' ')},*/	/* Old English (ca. 450-1100) -> Anglo-Saxon */
-  {"aoa",	HB_TAG('C','P','P',' ')},	/* Angolar -> Creoles */
-  {"apa",	HB_TAG('A','T','H',' ')},	/* Apache [collection] -> 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 */
-  {"apk",	HB_TAG('A','T','H',' ')},	/* Kiowa Apache -> Athapaskan */
-  {"apl",	HB_TAG('A','T','H',' ')},	/* Lipan Apache -> Athapaskan */
-  {"apm",	HB_TAG('A','T','H',' ')},	/* Mescalero-Chiricahua Apache -> Athapaskan */
-  {"apw",	HB_TAG('A','T','H',' ')},	/* Western Apache -> Athapaskan */
-  {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
-  {"arb",	HB_TAG('A','R','A',' ')},	/* Standard Arabic -> Arabic */
-  {"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 [collection] -> 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 */
-/*{"avn",	HB_TAG('A','V','N',' ')},*/	/* Avatime */
-/*{"awa",	HB_TAG('A','W','A',' ')},*/	/* Awadhi */
-  {"ay",	HB_TAG('A','Y','M',' ')},	/* Aymara [macrolanguage] */
-  {"ayc",	HB_TAG('A','Y','M',' ')},	/* Southern Aymara -> Aymara */
-  {"ayh",	HB_TAG('A','R','A',' ')},	/* Hadrami Arabic -> Arabic */
-  {"ayl",	HB_TAG('A','R','A',' ')},	/* Libyan Arabic -> Arabic */
-  {"ayn",	HB_TAG('A','R','A',' ')},	/* Sanaani Arabic -> Arabic */
-  {"ayp",	HB_TAG('A','R','A',' ')},	/* North Mesopotamian Arabic -> Arabic */
-  {"ayr",	HB_TAG('A','Y','M',' ')},	/* Central Aymara -> Aymara */
-  {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani [macrolanguage] */
-  {"azb",	HB_TAG('A','Z','B',' ')},	/* South Azerbaijani -> Torki */
-  {"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 [collection] */
-  {"bag",	HB_TAG_NONE	       },	/* Tuki != Baghelkhandi */
-  {"bah",	HB_TAG('C','P','P',' ')},	/* Bahamas Creole English -> Creoles */
-  {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [collection] */
-  {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolanguage] */
-/*{"ban",	HB_TAG('B','A','N',' ')},*/	/* Balinese */
-/*{"bar",	HB_TAG('B','A','R',' ')},*/	/* Bavarian */
-  {"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 */
-  {"bcr",	HB_TAG('A','T','H',' ')},	/* Babine -> Athapaskan */
-/*{"bdy",	HB_TAG('B','D','Y',' ')},*/	/* Bandjalang */
-  {"be",	HB_TAG('B','E','L',' ')},	/* Belarusian -> Belarussian */
-  {"bea",	HB_TAG('A','T','H',' ')},	/* Beaver -> Athapaskan */
-  {"beb",	HB_TAG('B','T','I',' ')},	/* Bebele -> Beti */
-/*{"bem",	HB_TAG('B','E','M',' ')},*/	/* Bemba (Zambia) */
-  {"ber",	HB_TAG('B','B','R',' ')},	/* Berber [collection] */
-  {"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 */
-  {"bfy",	HB_TAG('B','A','G',' ')},	/* Bagheli -> Baghelkhandi */
-  {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
-/*{"bgc",	HB_TAG('B','G','C',' ')},*/	/* Haryanvi */
-  {"bgn",	HB_TAG('B','L','I',' ')},	/* Western Balochi -> Baluchi */
-  {"bgp",	HB_TAG('B','L','I',' ')},	/* Eastern Balochi -> Baluchi */
-  {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
-  {"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 */
-  {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) -> Bikol */
-/*{"bho",	HB_TAG('B','H','O',' ')},*/	/* Bhojpuri */
-  {"bhr",	HB_TAG('M','L','G',' ')},	/* Bara Malagasy -> Malagasy */
-  {"bi",	HB_TAG('B','I','S',' ')},	/* Bislama */
-  {"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 */
-  {"blg",	HB_TAG('I','B','A',' ')},	/* Balau (retired code) -> Iban */
-  {"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 [collection] */
-  {"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','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','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 */
-  {"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 */
-  {"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 */
-  {"chm",	HB_TAG('H','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> High Mari */
-  {"chm",	HB_TAG('L','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> Low Mari */
-  {"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 */
-  {"chp",	HB_TAG('A','T','H',' ')},	/* Chipewyan -> Athapaskan */
-  {"chq",	HB_TAG('C','C','H','N')},	/* Quiotepec Chinantec -> Chinantec */
-/*{"chr",	HB_TAG('C','H','R',' ')},*/	/* Cherokee */
-/*{"chy",	HB_TAG('C','H','Y',' ')},*/	/* Cheyenne */
-  {"chz",	HB_TAG('C','C','H','N')},	/* Ozumacín Chinantec -> Chinantec */
-  {"ciw",	HB_TAG('O','J','B',' ')},	/* Chippewa -> Ojibway */
-/*{"cja",	HB_TAG('C','J','A',' ')},*/	/* Western Cham */
-/*{"cjm",	HB_TAG('C','J','M',' ')},*/	/* Eastern Cham */
-  {"cjy",	HB_TAG('Z','H','S',' ')},	/* Jinyu Chinese -> Chinese, Simplified */
-  {"cka",	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin (retired code) -> Chin */
-  {"ckb",	HB_TAG('K','U','R',' ')},	/* Central Kurdish -> Kurdish */
-  {"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 */
-  {"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 */
-  {"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 [collection] -> Creoles */
-  {"cpf",	HB_TAG('C','P','P',' ')},	/* French-based creoles and pidgins [collection] -> Creoles */
-  {"cpi",	HB_TAG('C','P','P',' ')},	/* Chinese Pidgin English -> Creoles */
-/*{"cpp",	HB_TAG('C','P','P',' ')},*/	/* Portuguese-based creoles and pidgins [collection] -> Creoles */
-  {"cpx",	HB_TAG('Z','H','S',' ')},	/* Pu-Xian Chinese -> Chinese, Simplified */
-  {"cqd",	HB_TAG('H','M','N',' ')},	/* Chuanqiandian Cluster Miao -> Hmong */
-  {"cqu",	HB_TAG('Q','U','H',' ')},	/* Chilean Quechua (retired code) -> Quechua (Bolivia) */
-  {"cqu",	HB_TAG('Q','U','Z',' ')},	/* Chilean Quechua (retired code) -> Quechua */
-  {"cr",	HB_TAG('C','R','E',' ')},	/* Cree [macrolanguage] */
-  {"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 [collection] -> 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 */
-  {"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 */
-/*{"ctt",	HB_TAG('C','T','T',' ')},*/	/* Wayanad Chetti */
-  {"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 */
-  {"cv",	HB_TAG('C','H','U',' ')},	/* Chuvash */
-  {"cvn",	HB_TAG('C','C','H','N')},	/* Valle Nacional Chinantec -> Chinantec */
-  {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
-  {"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 */
-  {"czt",	HB_TAG('Q','I','N',' ')},	/* Zotung Chin -> Chin */
-  {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
-/*{"dag",	HB_TAG('D','A','G',' ')},*/	/* Dagbani */
-  {"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 */
-  {"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('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 */
-  {"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) */
-  {"dwk",	HB_TAG('K','U','I',' ')},	/* Dawik Kui -> Kui */
-  {"dwu",	HB_TAG('D','U','J',' ')},	/* Dhuwal */
-  {"dwy",	HB_TAG('D','U','J',' ')},	/* Dhuwaya -> Dhuwal */
-  {"dyu",	HB_TAG('J','U','L',' ')},	/* Dyula -> Jula */
-  {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
-  {"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 */
-  {"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 */
-  {"esi",	HB_TAG('I','P','K',' ')},	/* North Alaskan Inupiatun -> Inupiat */
-  {"esk",	HB_TAG('I','P','K',' ')},	/* Northwest Alaska Inupiatun -> Inupiat */
-/*{"esu",	HB_TAG('E','S','U',' ')},*/	/* Central Yupik */
-  {"et",	HB_TAG('E','T','I',' ')},	/* Estonian [macrolanguage] */
-  {"eto",	HB_TAG('B','T','I',' ')},	/* Eton (Cameroon) -> Beti */
-  {"eu",	HB_TAG('E','U','Q',' ')},	/* Basque */
-  {"euq",	HB_TAG_NONE	       },	/* Basque [collection] != 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) */
-  {"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 */
-  {"fi",	HB_TAG('F','I','N',' ')},	/* Finnish */
-  {"fil",	HB_TAG('P','I','L',' ')},	/* Filipino */
-  {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
-  {"flm",	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) (retired code) */
-  {"flm",	HB_TAG('Q','I','N',' ')},	/* Falam Chin (retired code) -> Chin */
-  {"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 */
-  {"fub",	HB_TAG('F','U','L',' ')},	/* Adamawa Fulfulde -> Fulah */
-  {"fuc",	HB_TAG('F','U','L',' ')},	/* Pulaar -> Fulah */
-  {"fue",	HB_TAG('F','U','L',' ')},	/* Borgu Fulfulde -> Fulah */
-  {"fuf",	HB_TAG('F','T','A',' ')},	/* Pular -> Futa */
-  {"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','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 */
-  {"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('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 */
-  {"hai",	HB_TAG('H','A','I','0')},	/* Haida [macrolanguage] */
-  {"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 */
-  {"hax",	HB_TAG('H','A','I','0')},	/* Southern Haida -> Haida */
-/*{"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 */
-  {"hdn",	HB_TAG('H','A','I','0')},	/* Northern Haida -> Haida */
-  {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
-  {"hea",	HB_TAG('H','M','N',' ')},	/* Northern Qiandong Miao -> Hmong */
-/*{"hei",	HB_TAG('H','E','I',' ')},*/	/* Heiltsuk */
-  {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
-/*{"hil",	HB_TAG('H','I','L',' ')},*/	/* Hiligaynon */
-  {"hji",	HB_TAG('M','L','Y',' ')},	/* Haji -> Malay */
-  {"hlt",	HB_TAG('Q','I','N',' ')},	/* Matu Chin -> Chin */
-  {"hma",	HB_TAG('H','M','N',' ')},	/* Southern Mashan Hmong -> Hmong */
-  {"hmc",	HB_TAG('H','M','N',' ')},	/* Central Huishui Hmong -> Hmong */
-  {"hmd",	HB_TAG('H','M','D',' ')},	/* Large Flowery Miao -> A-Hmao */
-  {"hmd",	HB_TAG('H','M','N',' ')},	/* Large Flowery Miao -> Hmong */
-  {"hme",	HB_TAG('H','M','N',' ')},	/* Eastern Huishui Hmong -> Hmong */
-  {"hmg",	HB_TAG('H','M','N',' ')},	/* Southwestern Guiyang Hmong -> Hmong */
-  {"hmh",	HB_TAG('H','M','N',' ')},	/* Southwestern Huishui Hmong -> Hmong */
-  {"hmi",	HB_TAG('H','M','N',' ')},	/* Northern Huishui Hmong -> Hmong */
-  {"hmj",	HB_TAG('H','M','N',' ')},	/* Ge -> Hmong */
-  {"hml",	HB_TAG('H','M','N',' ')},	/* Luopohe Hmong -> Hmong */
-  {"hmm",	HB_TAG('H','M','N',' ')},	/* Central Mashan Hmong -> Hmong */
-/*{"hmn",	HB_TAG('H','M','N',' ')},*/	/* Hmong [macrolanguage] */
-  {"hmp",	HB_TAG('H','M','N',' ')},	/* Northern Mashan Hmong -> Hmong */
-  {"hmq",	HB_TAG('H','M','N',' ')},	/* Eastern Qiandong Miao -> Hmong */
-  {"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 */
-  {"hmz",	HB_TAG('H','M','Z',' ')},	/* Hmong Shua -> Hmong Shuat */
-  {"hmz",	HB_TAG('H','M','N',' ')},	/* Hmong Shua -> Hmong */
-/*{"hnd",	HB_TAG('H','N','D',' ')},*/	/* Southern Hindko -> Hindko */
-  {"hne",	HB_TAG('C','H','H',' ')},	/* Chhattisgarhi -> Chattisgarhi */
-  {"hnj",	HB_TAG('H','M','N',' ')},	/* Hmong Njua -> Hmong */
-  {"hno",	HB_TAG('H','N','D',' ')},	/* Northern Hindko -> Hindko */
-  {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
-  {"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 */
-  {"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 */
-  {"hz",	HB_TAG('H','E','R',' ')},	/* Herero */
-  {"ia",	HB_TAG('I','N','A',' ')},	/* Interlingua (International Auxiliary Language Association) */
-/*{"iba",	HB_TAG('I','B','A',' ')},*/	/* Iban */
-/*{"ibb",	HB_TAG('I','B','B',' ')},*/	/* Ibibio */
-  {"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 [collection] */
-  {"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 */
-  {"ike",	HB_TAG('I','N','U','K')},	/* Eastern Canadian Inuktitut -> Nunavik 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 */
-/*{"iru",	HB_TAG('I','R','U',' ')},*/	/* Irula */
-  {"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] */
-  {"iu",	HB_TAG('I','N','U','K')},	/* Inuktitut [macrolanguage] -> Nunavik Inuktitut */
-  {"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('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 [collection] */
-/*{"kaw",	HB_TAG('K','A','W',' ')},*/	/* Kawi (Old Javanese) */
-  {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
-  {"kby",	HB_TAG('K','N','R',' ')},	/* Manga Kanuri -> Kanuri */
-  {"kca",	HB_TAG('K','H','K',' ')},	/* Khanty -> Khanty-Kazim */
-  {"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) */
-  {"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) */
-  {"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','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('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','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 */
-/*{"kwk",	HB_TAG('K','W','K',' ')},*/	/* Kwakiutl -> Kwakʼwala */
-  {"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) */
-  {"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) (retired code) != 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 */
-  {"lbl",	HB_TAG('B','I','K',' ')},	/* Libon Bikol -> Bikol */
-  {"lce",	HB_TAG('M','L','Y',' ')},	/* Loncong -> Malay */
-  {"lcf",	HB_TAG('M','L','Y',' ')},	/* Lubu -> Malay */
-  {"ldi",	HB_TAG('K','O','N','0')},	/* Laari -> Kongo */
-  {"ldk",	HB_TAG_NONE	       },	/* Leelau != Ladakhi */
-/*{"lef",	HB_TAG('L','E','F',' ')},*/	/* Lelemi */
-/*{"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 */
-/*{"lpo",	HB_TAG('L','P','O',' ')},*/	/* Lipo */
-/*{"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 */
-  {"lsb",	HB_TAG_NONE	       },	/* Burundian Sign Language != Lower Sorbian */
-  {"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 */
-  {"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','Y','N',' ')},	/* Mam -> Mayan */
-  {"man",	HB_TAG('M','N','K',' ')},	/* Mandingo [macrolanguage] -> Maninka */
-  {"map",	HB_TAG_NONE	       },	/* Austronesian [collection] != 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','L','Y',' ')},	/* Pattani Malay -> Malay */
-  {"mfb",	HB_TAG('M','L','Y',' ')},	/* Bangka -> Malay */
-  {"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','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 */
-  {"mns",	HB_TAG('M','A','N',' ')},	/* Mansi */
-  {"mnw",	HB_TAG('M','O','N',' ')},	/* Mon */
-  {"mnw",	HB_TAG('M','O','N','T')},	/* Mon -> Thailand Mon */
-  {"mnx",	HB_TAG_NONE	       },	/* Manikion != Manx */
-  {"mo",	HB_TAG('M','O','L',' ')},	/* Moldavian (retired code) */
-  {"mo",	HB_TAG('R','O','M',' ')},	/* Moldavian (retired code) -> Romanian */
-  {"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 */
-  {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
-  {"mrh",	HB_TAG('Q','I','N',' ')},	/* Mara Chin -> Chin */
-  {"mrj",	HB_TAG('H','M','A',' ')},	/* Western Mari -> High Mari */
-  {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
-  {"msc",	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka -> Maninka */
-  {"msh",	HB_TAG('M','L','G',' ')},	/* Masikoro Malagasy -> Malagasy */
-  {"msi",	HB_TAG('M','L','Y',' ')},	/* Sabah Malay -> Malay */
-  {"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 [collection] != 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 */
-  {"mvb",	HB_TAG('A','T','H',' ')},	/* Mattole -> Athapaskan */
-  {"mve",	HB_TAG('M','A','W',' ')},	/* Marwari (Pakistan) */
-  {"mvf",	HB_TAG('M','N','G',' ')},	/* Peripheral Mongolian -> Mongolian */
-  {"mwk",	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan -> Maninka */
-/*{"mwl",	HB_TAG('M','W','L',' ')},*/	/* Mirandese */
-  {"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('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 [collection] */
-  {"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('C','P','P',' ')},	/* Naga Pidgin -> Creoles */
-/*{"nah",	HB_TAG('N','A','H',' ')},*/	/* Nahuatl [collection] */
-  {"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 (retired code) -> 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) */
-  {"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 */
-  {"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 */
-  {"nyn",	HB_TAG('N','K','L',' ')},	/* Nyankole */
-/*{"nza",	HB_TAG('N','Z','A',' ')},*/	/* Tigon Mbembe -> Mbembe Tigon */
-  {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
-  {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] -> Ojibway */
-/*{"ojb",	HB_TAG('O','J','B',' ')},*/	/* Northwestern Ojibwa -> Ojibway */
-  {"ojc",	HB_TAG('O','J','B',' ')},	/* Central Ojibwa -> Ojibway */
-  {"ojg",	HB_TAG('O','J','B',' ')},	/* Eastern Ojibwa -> Ojibway */
-  {"ojs",	HB_TAG('O','C','R',' ')},	/* Severn Ojibwa -> Oji-Cree */
-  {"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 [collection] != 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 */
-/*{"pcc",	HB_TAG('P','C','C',' ')},*/	/* Bouyei */
-/*{"pcd",	HB_TAG('P','C','D',' ')},*/	/* Picard */
-  {"pce",	HB_TAG('P','L','G',' ')},	/* Ruching Palaung -> Palaung */
-  {"pck",	HB_TAG('Q','I','N',' ')},	/* Paite Chin -> Chin */
-  {"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('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 */
-  {"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 */
-  {"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) */
-  {"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) */
-  {"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) */
-  {"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','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 */
-/*{"rhg",	HB_TAG('R','H','G',' ')},*/	/* Rohingya */
-/*{"ria",	HB_TAG('R','I','A',' ')},*/	/* Riang (India) */
-  {"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 */
-  {"rm",	HB_TAG('R','M','S',' ')},	/* Romansh */
-  {"rmc",	HB_TAG('R','O','Y',' ')},	/* Carpathian Romani -> Romany */
-  {"rmf",	HB_TAG('R','O','Y',' ')},	/* Kalo Finnish Romani -> Romany */
-  {"rml",	HB_TAG('R','O','Y',' ')},	/* Baltic Romani -> Romany */
-  {"rmn",	HB_TAG('R','O','Y',' ')},	/* Balkan Romani -> Romany */
-  {"rmo",	HB_TAG('R','O','Y',' ')},	/* Sinte Romani -> Romany */
-  {"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','O','Y',' ')},	/* Vlax Romani -> Romany */
-  {"rmz",	HB_TAG('A','R','K',' ')},	/* Marma -> Rakhine */
-  {"rn",	HB_TAG('R','U','N',' ')},	/* Rundi */
-  {"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 */
-/*{"rup",	HB_TAG('R','U','P',' ')},*/	/* Aromanian */
-  {"rw",	HB_TAG('R','U','A',' ')},	/* Kinyarwanda */
-  {"rwr",	HB_TAG('M','A','W',' ')},	/* Marwari (India) */
-  {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
-  {"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 */
-  {"scs",	HB_TAG('S','C','S',' ')},	/* North Slavey */
-  {"scs",	HB_TAG('S','L','A',' ')},	/* North Slavey -> Slavey */
-  {"scs",	HB_TAG('A','T','H',' ')},	/* North Slavey -> Athapaskan */
-  {"sd",	HB_TAG('S','N','D',' ')},	/* Sindhi */
-  {"sdc",	HB_TAG('S','R','D',' ')},	/* Sassarese Sardinian -> Sardinian */
-  {"sdh",	HB_TAG('K','U','R',' ')},	/* Southern Kurdish -> Kurdish */
-  {"sdn",	HB_TAG('S','R','D',' ')},	/* Gallurese Sardinian -> Sardinian */
-  {"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 */
-/*{"sel",	HB_TAG('S','E','L',' ')},*/	/* Selkup */
-  {"sez",	HB_TAG('Q','I','N',' ')},	/* Senthang Chin -> Chin */
-  {"sfm",	HB_TAG('S','F','M',' ')},	/* Small Flowery Miao */
-  {"sfm",	HB_TAG('H','M','N',' ')},	/* Small Flowery Miao -> Hmong */
-  {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
-/*{"sga",	HB_TAG('S','G','A',' ')},*/	/* Old Irish (to 900) */
-  {"sgc",	HB_TAG('K','A','L',' ')},	/* Kipsigis -> Kalenjin */
-  {"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 */
-  {"sh",	HB_TAG('B','O','S',' ')},	/* Serbo-Croatian [macrolanguage] -> Bosnian */
-  {"sh",	HB_TAG('H','R','V',' ')},	/* Serbo-Croatian [macrolanguage] -> Croatian */
-  {"sh",	HB_TAG('S','R','B',' ')},	/* Serbo-Croatian [macrolanguage] -> Serbian */
-  {"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 [collection] != Slavey */
-  {"sm",	HB_TAG('S','M','O',' ')},	/* Samoan */
-  {"sma",	HB_TAG('S','S','M',' ')},	/* Southern Sami */
-  {"smd",	HB_TAG('M','B','N',' ')},	/* Sama (retired code) -> Mbundu */
-  {"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 */
-  {"snb",	HB_TAG('I','B','A',' ')},	/* Sebuyau (retired code) -> Iban */
-  {"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 */
-  {"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 */
-/*{"syl",	HB_TAG('S','Y','L',' ')},*/	/* Sylheti */
-/*{"syr",	HB_TAG('S','Y','R',' ')},*/	/* Syriac [macrolanguage] */
-/*{"szl",	HB_TAG('S','Z','L',' ')},*/	/* Silesian */
-  {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
-  {"taa",	HB_TAG('A','T','H',' ')},	/* Lower Tanana -> Athapaskan */
-/*{"tab",	HB_TAG('T','A','B',' ')},*/	/* Tabassaran -> Tabasaran */
-  {"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 */
-  {"tdx",	HB_TAG('M','L','G',' ')},	/* Tandroy-Mahafaly Malagasy -> Malagasy */
-  {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
-  {"tec",	HB_TAG('K','A','L',' ')},	/* Terik -> Kalenjin */
-  {"tem",	HB_TAG('T','M','N',' ')},	/* Timne -> Temne */
-/*{"tet",	HB_TAG('T','E','T',' ')},*/	/* Tetum */
-  {"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 */
-/*{"tjl",	HB_TAG('T','J','L',' ')},*/	/* Tai Laing */
-  {"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 */
-/*{"tli",	HB_TAG('T','L','I',' ')},*/	/* Tlingit */
-  {"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 */
-  {"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 [collection] != 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 */
-  {"txy",	HB_TAG('M','L','G',' ')},	/* Tanosy Malagasy -> Malagasy */
-  {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
-  {"tyv",	HB_TAG('T','U','V',' ')},	/* Tuvinian -> Tuvin */
-/*{"tyz",	HB_TAG('T','Y','Z',' ')},*/	/* Tày */
-  {"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 */
-/*{"wci",	HB_TAG('W','C','I',' ')},*/	/* Waci Gbe */
-  {"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 */
-  {"wni",	HB_TAG('C','M','R',' ')},	/* Ndzwani Comorian -> Comorian */
-  {"wo",	HB_TAG('W','L','F',' ')},	/* Wolof */
-  {"wry",	HB_TAG('M','A','W',' ')},	/* Merwari -> Marwari */
-  {"wsg",	HB_TAG('G','O','N',' ')},	/* Adilabad Gondi -> Gondi */
-/*{"wtm",	HB_TAG('W','T','M',' ')},*/	/* Mewati */
-  {"wuu",	HB_TAG('Z','H','S',' ')},	/* Wu Chinese -> Chinese, Simplified */
-  {"xal",	HB_TAG('K','L','M',' ')},	/* Kalmyk */
-  {"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 */
-  {"xnj",	HB_TAG('S','X','T',' ')},	/* Ngoni (Tanzania) -> Sutu */
-  {"xnq",	HB_TAG('S','X','T',' ')},	/* Ngoni (Mozambique) -> Sutu */
-  {"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('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 */
-/*{"xub",	HB_TAG('X','U','B',' ')},*/	/* Betta Kurumba -> Bette Kuruma */
-/*{"xuj",	HB_TAG('X','U','J',' ')},*/	/* Jennu Kurumba -> Jennu Kuruma */
-  {"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 */
-/*{"ygp",	HB_TAG('Y','G','P',' ')},*/	/* Gepo */
-  {"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 */
-/*{"yna",	HB_TAG('Y','N','A',' ')},*/	/* Aluo */
-  {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
-  {"yos",	HB_TAG('Q','I','N',' ')},	/* Yos (retired code) -> Chin */
-  {"yua",	HB_TAG('M','Y','N',' ')},	/* Yucateco -> Mayan */
-  {"yue",	HB_TAG('Z','H','H',' ')},	/* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */
-/*{"ywq",	HB_TAG('Y','W','Q',' ')},*/	/* Wuding-Luquan Yi */
-  {"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('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, 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 */
-  {"zlm",	HB_TAG('M','L','Y',' ')},	/* Malay */
-  {"zln",	HB_TAG('Z','H','A',' ')},	/* Lianshan Zhuang -> Zhuang */
-  {"zlq",	HB_TAG('Z','H','A',' ')},	/* Liuqian Zhuang -> Zhuang */
-  {"zmi",	HB_TAG('M','L','Y',' ')},	/* Negeri Sembilan Malay -> Malay */
-  {"zmz",	HB_TAG('B','A','D','0')},	/* Mbandja -> Banda */
-  {"znd",	HB_TAG_NONE	       },	/* Zande [collection] != 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 */
-  {"zsm",	HB_TAG('M','L','Y',' ')},	/* Standard Malay -> Malay */
-  {"zu",	HB_TAG('Z','U','L',' ')},	/* Zulu */
-  {"zum",	HB_TAG('L','R','C',' ')},	/* Kumzari -> Luri */
-  {"zyb",	HB_TAG('Z','H','A',' ')},	/* Yongbei Zhuang -> Zhuang */
-  {"zyg",	HB_TAG('Z','H','A',' ')},	/* Yang Zhuang -> Zhuang */
-  {"zyj",	HB_TAG('Z','H','A',' ')},	/* Youjiang Zhuang -> Zhuang */
-  {"zyn",	HB_TAG('Z','H','A',' ')},	/* Yongnan Zhuang -> Zhuang */
-  {"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 */
+static const LangTag ot_languages2[] = {
+  {HB_TAG('a','a',' ',' '),	HB_TAG('A','F','R',' ')},	/* Afar */
+  {HB_TAG('a','b',' ',' '),	HB_TAG('A','B','K',' ')},	/* Abkhazian */
+  {HB_TAG('a','f',' ',' '),	HB_TAG('A','F','K',' ')},	/* Afrikaans */
+  {HB_TAG('a','k',' ',' '),	HB_TAG('A','K','A',' ')},	/* Akan [macrolanguage] */
+  {HB_TAG('a','m',' ',' '),	HB_TAG('A','M','H',' ')},	/* Amharic */
+  {HB_TAG('a','n',' ',' '),	HB_TAG('A','R','G',' ')},	/* Aragonese */
+  {HB_TAG('a','r',' ',' '),	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
+  {HB_TAG('a','s',' ',' '),	HB_TAG('A','S','M',' ')},	/* Assamese */
+  {HB_TAG('a','v',' ',' '),	HB_TAG('A','V','R',' ')},	/* Avaric -> Avar */
+  {HB_TAG('a','y',' ',' '),	HB_TAG('A','Y','M',' ')},	/* Aymara [macrolanguage] */
+  {HB_TAG('a','z',' ',' '),	HB_TAG('A','Z','E',' ')},	/* Azerbaijani [macrolanguage] */
+  {HB_TAG('b','a',' ',' '),	HB_TAG('B','S','H',' ')},	/* Bashkir */
+  {HB_TAG('b','e',' ',' '),	HB_TAG('B','E','L',' ')},	/* Belarusian -> Belarussian */
+  {HB_TAG('b','g',' ',' '),	HB_TAG('B','G','R',' ')},	/* Bulgarian */
+  {HB_TAG('b','i',' ',' '),	HB_TAG('B','I','S',' ')},	/* Bislama */
+  {HB_TAG('b','i',' ',' '),	HB_TAG('C','P','P',' ')},	/* Bislama -> Creoles */
+  {HB_TAG('b','m',' ',' '),	HB_TAG('B','M','B',' ')},	/* Bambara (Bamanankan) */
+  {HB_TAG('b','n',' ',' '),	HB_TAG('B','E','N',' ')},	/* Bengali */
+  {HB_TAG('b','o',' ',' '),	HB_TAG('T','I','B',' ')},	/* Tibetan */
+  {HB_TAG('b','r',' ',' '),	HB_TAG('B','R','E',' ')},	/* Breton */
+  {HB_TAG('b','s',' ',' '),	HB_TAG('B','O','S',' ')},	/* Bosnian */
+  {HB_TAG('c','a',' ',' '),	HB_TAG('C','A','T',' ')},	/* Catalan */
+  {HB_TAG('c','e',' ',' '),	HB_TAG('C','H','E',' ')},	/* Chechen */
+  {HB_TAG('c','h',' ',' '),	HB_TAG('C','H','A',' ')},	/* Chamorro */
+  {HB_TAG('c','o',' ',' '),	HB_TAG('C','O','S',' ')},	/* Corsican */
+  {HB_TAG('c','r',' ',' '),	HB_TAG('C','R','E',' ')},	/* Cree [macrolanguage] */
+  {HB_TAG('c','s',' ',' '),	HB_TAG('C','S','Y',' ')},	/* Czech */
+  {HB_TAG('c','u',' ',' '),	HB_TAG('C','S','L',' ')},	/* Church Slavonic */
+  {HB_TAG('c','v',' ',' '),	HB_TAG('C','H','U',' ')},	/* Chuvash */
+  {HB_TAG('c','y',' ',' '),	HB_TAG('W','E','L',' ')},	/* Welsh */
+  {HB_TAG('d','a',' ',' '),	HB_TAG('D','A','N',' ')},	/* Danish */
+  {HB_TAG('d','e',' ',' '),	HB_TAG('D','E','U',' ')},	/* German */
+  {HB_TAG('d','v',' ',' '),	HB_TAG('D','I','V',' ')},	/* Divehi (Dhivehi, Maldivian) */
+  {HB_TAG('d','v',' ',' '),	HB_TAG('D','H','V',' ')},	/* Divehi (Dhivehi, Maldivian) (deprecated) */
+  {HB_TAG('d','z',' ',' '),	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
+  {HB_TAG('e','e',' ',' '),	HB_TAG('E','W','E',' ')},	/* Ewe */
+  {HB_TAG('e','l',' ',' '),	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) -> Greek */
+  {HB_TAG('e','n',' ',' '),	HB_TAG('E','N','G',' ')},	/* English */
+  {HB_TAG('e','o',' ',' '),	HB_TAG('N','T','O',' ')},	/* Esperanto */
+  {HB_TAG('e','s',' ',' '),	HB_TAG('E','S','P',' ')},	/* Spanish */
+  {HB_TAG('e','t',' ',' '),	HB_TAG('E','T','I',' ')},	/* Estonian [macrolanguage] */
+  {HB_TAG('e','u',' ',' '),	HB_TAG('E','U','Q',' ')},	/* Basque */
+  {HB_TAG('f','a',' ',' '),	HB_TAG('F','A','R',' ')},	/* Persian [macrolanguage] */
+  {HB_TAG('f','f',' ',' '),	HB_TAG('F','U','L',' ')},	/* Fulah [macrolanguage] */
+  {HB_TAG('f','i',' ',' '),	HB_TAG('F','I','N',' ')},	/* Finnish */
+  {HB_TAG('f','j',' ',' '),	HB_TAG('F','J','I',' ')},	/* Fijian */
+  {HB_TAG('f','o',' ',' '),	HB_TAG('F','O','S',' ')},	/* Faroese */
+  {HB_TAG('f','r',' ',' '),	HB_TAG('F','R','A',' ')},	/* French */
+  {HB_TAG('f','y',' ',' '),	HB_TAG('F','R','I',' ')},	/* Western Frisian -> Frisian */
+  {HB_TAG('g','a',' ',' '),	HB_TAG('I','R','I',' ')},	/* Irish */
+  {HB_TAG('g','d',' ',' '),	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic (Gaelic) */
+  {HB_TAG('g','l',' ',' '),	HB_TAG('G','A','L',' ')},	/* Galician */
+  {HB_TAG('g','n',' ',' '),	HB_TAG('G','U','A',' ')},	/* Guarani [macrolanguage] */
+  {HB_TAG('g','u',' ',' '),	HB_TAG('G','U','J',' ')},	/* Gujarati */
+  {HB_TAG('g','v',' ',' '),	HB_TAG('M','N','X',' ')},	/* Manx */
+  {HB_TAG('h','a',' ',' '),	HB_TAG('H','A','U',' ')},	/* Hausa */
+  {HB_TAG('h','e',' ',' '),	HB_TAG('I','W','R',' ')},	/* Hebrew */
+  {HB_TAG('h','i',' ',' '),	HB_TAG('H','I','N',' ')},	/* Hindi */
+  {HB_TAG('h','o',' ',' '),	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
+  {HB_TAG('h','o',' ',' '),	HB_TAG('C','P','P',' ')},	/* Hiri Motu -> Creoles */
+  {HB_TAG('h','r',' ',' '),	HB_TAG('H','R','V',' ')},	/* Croatian */
+  {HB_TAG('h','t',' ',' '),	HB_TAG('H','A','I',' ')},	/* Haitian (Haitian Creole) */
+  {HB_TAG('h','t',' ',' '),	HB_TAG('C','P','P',' ')},	/* Haitian -> Creoles */
+  {HB_TAG('h','u',' ',' '),	HB_TAG('H','U','N',' ')},	/* Hungarian */
+  {HB_TAG('h','y',' ',' '),	HB_TAG('H','Y','E','0')},	/* Armenian -> Armenian East */
+  {HB_TAG('h','y',' ',' '),	HB_TAG('H','Y','E',' ')},	/* Armenian */
+  {HB_TAG('h','z',' ',' '),	HB_TAG('H','E','R',' ')},	/* Herero */
+  {HB_TAG('i','a',' ',' '),	HB_TAG('I','N','A',' ')},	/* Interlingua (International Auxiliary Language Association) */
+  {HB_TAG('i','d',' ',' '),	HB_TAG('I','N','D',' ')},	/* Indonesian */
+  {HB_TAG('i','d',' ',' '),	HB_TAG('M','L','Y',' ')},	/* Indonesian -> Malay */
+  {HB_TAG('i','e',' ',' '),	HB_TAG('I','L','E',' ')},	/* Interlingue */
+  {HB_TAG('i','g',' ',' '),	HB_TAG('I','B','O',' ')},	/* Igbo */
+  {HB_TAG('i','i',' ',' '),	HB_TAG('Y','I','M',' ')},	/* Sichuan Yi -> Yi Modern */
+  {HB_TAG('i','k',' ',' '),	HB_TAG('I','P','K',' ')},	/* Inupiaq [macrolanguage] -> Inupiat */
+  {HB_TAG('i','n',' ',' '),	HB_TAG('I','N','D',' ')},	/* Indonesian (retired code) */
+  {HB_TAG('i','n',' ',' '),	HB_TAG('M','L','Y',' ')},	/* Indonesian (retired code) -> Malay */
+  {HB_TAG('i','o',' ',' '),	HB_TAG('I','D','O',' ')},	/* Ido */
+  {HB_TAG('i','s',' ',' '),	HB_TAG('I','S','L',' ')},	/* Icelandic */
+  {HB_TAG('i','t',' ',' '),	HB_TAG('I','T','A',' ')},	/* Italian */
+  {HB_TAG('i','u',' ',' '),	HB_TAG('I','N','U',' ')},	/* Inuktitut [macrolanguage] */
+  {HB_TAG('i','u',' ',' '),	HB_TAG('I','N','U','K')},	/* Inuktitut [macrolanguage] -> Nunavik Inuktitut */
+  {HB_TAG('i','w',' ',' '),	HB_TAG('I','W','R',' ')},	/* Hebrew (retired code) */
+  {HB_TAG('j','a',' ',' '),	HB_TAG('J','A','N',' ')},	/* Japanese */
+  {HB_TAG('j','i',' ',' '),	HB_TAG('J','I','I',' ')},	/* Yiddish (retired code) */
+  {HB_TAG('j','v',' ',' '),	HB_TAG('J','A','V',' ')},	/* Javanese */
+  {HB_TAG('j','w',' ',' '),	HB_TAG('J','A','V',' ')},	/* Javanese (retired code) */
+  {HB_TAG('k','a',' ',' '),	HB_TAG('K','A','T',' ')},	/* Georgian */
+  {HB_TAG('k','g',' ',' '),	HB_TAG('K','O','N','0')},	/* Kongo [macrolanguage] */
+  {HB_TAG('k','i',' ',' '),	HB_TAG('K','I','K',' ')},	/* Kikuyu (Gikuyu) */
+  {HB_TAG('k','j',' ',' '),	HB_TAG('K','U','A',' ')},	/* Kuanyama */
+  {HB_TAG('k','k',' ',' '),	HB_TAG('K','A','Z',' ')},	/* Kazakh */
+  {HB_TAG('k','l',' ',' '),	HB_TAG('G','R','N',' ')},	/* Greenlandic */
+  {HB_TAG('k','m',' ',' '),	HB_TAG('K','H','M',' ')},	/* Khmer */
+  {HB_TAG('k','n',' ',' '),	HB_TAG('K','A','N',' ')},	/* Kannada */
+  {HB_TAG('k','o',' ',' '),	HB_TAG('K','O','R',' ')},	/* Korean */
+  {HB_TAG('k','o',' ',' '),	HB_TAG('K','O','H',' ')},	/* Korean -> Korean Old Hangul */
+  {HB_TAG('k','r',' ',' '),	HB_TAG('K','N','R',' ')},	/* Kanuri [macrolanguage] */
+  {HB_TAG('k','s',' ',' '),	HB_TAG('K','S','H',' ')},	/* Kashmiri */
+  {HB_TAG('k','u',' ',' '),	HB_TAG('K','U','R',' ')},	/* Kurdish [macrolanguage] */
+  {HB_TAG('k','v',' ',' '),	HB_TAG('K','O','M',' ')},	/* Komi [macrolanguage] */
+  {HB_TAG('k','w',' ',' '),	HB_TAG('C','O','R',' ')},	/* Cornish */
+  {HB_TAG('k','y',' ',' '),	HB_TAG('K','I','R',' ')},	/* Kirghiz (Kyrgyz) */
+  {HB_TAG('l','a',' ',' '),	HB_TAG('L','A','T',' ')},	/* Latin */
+  {HB_TAG('l','b',' ',' '),	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
+  {HB_TAG('l','g',' ',' '),	HB_TAG('L','U','G',' ')},	/* Ganda */
+  {HB_TAG('l','i',' ',' '),	HB_TAG('L','I','M',' ')},	/* Limburgish */
+  {HB_TAG('l','n',' ',' '),	HB_TAG('L','I','N',' ')},	/* Lingala */
+  {HB_TAG('l','o',' ',' '),	HB_TAG('L','A','O',' ')},	/* Lao */
+  {HB_TAG('l','t',' ',' '),	HB_TAG('L','T','H',' ')},	/* Lithuanian */
+  {HB_TAG('l','u',' ',' '),	HB_TAG('L','U','B',' ')},	/* Luba-Katanga */
+  {HB_TAG('l','v',' ',' '),	HB_TAG('L','V','I',' ')},	/* Latvian [macrolanguage] */
+  {HB_TAG('m','g',' ',' '),	HB_TAG('M','L','G',' ')},	/* Malagasy [macrolanguage] */
+  {HB_TAG('m','h',' ',' '),	HB_TAG('M','A','H',' ')},	/* Marshallese */
+  {HB_TAG('m','i',' ',' '),	HB_TAG('M','R','I',' ')},	/* Maori */
+  {HB_TAG('m','k',' ',' '),	HB_TAG('M','K','D',' ')},	/* Macedonian */
+  {HB_TAG('m','l',' ',' '),	HB_TAG('M','A','L',' ')},	/* Malayalam -> Malayalam Traditional */
+  {HB_TAG('m','l',' ',' '),	HB_TAG('M','L','R',' ')},	/* Malayalam -> Malayalam Reformed */
+  {HB_TAG('m','n',' ',' '),	HB_TAG('M','N','G',' ')},	/* Mongolian [macrolanguage] */
+  {HB_TAG('m','o',' ',' '),	HB_TAG('M','O','L',' ')},	/* Moldavian (retired code) */
+  {HB_TAG('m','o',' ',' '),	HB_TAG('R','O','M',' ')},	/* Moldavian (retired code) -> Romanian */
+  {HB_TAG('m','r',' ',' '),	HB_TAG('M','A','R',' ')},	/* Marathi */
+  {HB_TAG('m','s',' ',' '),	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
+  {HB_TAG('m','t',' ',' '),	HB_TAG('M','T','S',' ')},	/* Maltese */
+  {HB_TAG('m','y',' ',' '),	HB_TAG('B','R','M',' ')},	/* Burmese */
+  {HB_TAG('n','a',' ',' '),	HB_TAG('N','A','U',' ')},	/* Nauru -> Nauruan */
+  {HB_TAG('n','b',' ',' '),	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål -> Norwegian */
+  {HB_TAG('n','d',' ',' '),	HB_TAG('N','D','B',' ')},	/* North Ndebele -> Ndebele */
+  {HB_TAG('n','e',' ',' '),	HB_TAG('N','E','P',' ')},	/* Nepali [macrolanguage] */
+  {HB_TAG('n','g',' ',' '),	HB_TAG('N','D','G',' ')},	/* Ndonga */
+  {HB_TAG('n','l',' ',' '),	HB_TAG('N','L','D',' ')},	/* Dutch */
+  {HB_TAG('n','n',' ',' '),	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk (Nynorsk, Norwegian) */
+  {HB_TAG('n','o',' ',' '),	HB_TAG('N','O','R',' ')},	/* Norwegian [macrolanguage] */
+  {HB_TAG('n','r',' ',' '),	HB_TAG('N','D','B',' ')},	/* South Ndebele -> Ndebele */
+  {HB_TAG('n','v',' ',' '),	HB_TAG('N','A','V',' ')},	/* Navajo */
+  {HB_TAG('n','v',' ',' '),	HB_TAG('A','T','H',' ')},	/* Navajo -> Athapaskan */
+  {HB_TAG('n','y',' ',' '),	HB_TAG('C','H','I',' ')},	/* Chichewa (Chewa, Nyanja) */
+  {HB_TAG('o','c',' ',' '),	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
+  {HB_TAG('o','j',' ',' '),	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] -> Ojibway */
+  {HB_TAG('o','m',' ',' '),	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
+  {HB_TAG('o','r',' ',' '),	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) [macrolanguage] */
+  {HB_TAG('o','s',' ',' '),	HB_TAG('O','S','S',' ')},	/* Ossetian */
+  {HB_TAG('p','a',' ',' '),	HB_TAG('P','A','N',' ')},	/* Punjabi */
+  {HB_TAG('p','i',' ',' '),	HB_TAG('P','A','L',' ')},	/* Pali */
+  {HB_TAG('p','l',' ',' '),	HB_TAG('P','L','K',' ')},	/* Polish */
+  {HB_TAG('p','s',' ',' '),	HB_TAG('P','A','S',' ')},	/* Pashto [macrolanguage] */
+  {HB_TAG('p','t',' ',' '),	HB_TAG('P','T','G',' ')},	/* Portuguese */
+  {HB_TAG('q','u',' ',' '),	HB_TAG('Q','U','Z',' ')},	/* Quechua [macrolanguage] */
+  {HB_TAG('r','m',' ',' '),	HB_TAG('R','M','S',' ')},	/* Romansh */
+  {HB_TAG('r','n',' ',' '),	HB_TAG('R','U','N',' ')},	/* Rundi */
+  {HB_TAG('r','o',' ',' '),	HB_TAG('R','O','M',' ')},	/* Romanian */
+  {HB_TAG('r','u',' ',' '),	HB_TAG('R','U','S',' ')},	/* Russian */
+  {HB_TAG('r','w',' ',' '),	HB_TAG('R','U','A',' ')},	/* Kinyarwanda */
+  {HB_TAG('s','a',' ',' '),	HB_TAG('S','A','N',' ')},	/* Sanskrit */
+  {HB_TAG('s','c',' ',' '),	HB_TAG('S','R','D',' ')},	/* Sardinian [macrolanguage] */
+  {HB_TAG('s','d',' ',' '),	HB_TAG('S','N','D',' ')},	/* Sindhi */
+  {HB_TAG('s','e',' ',' '),	HB_TAG('N','S','M',' ')},	/* Northern Sami */
+  {HB_TAG('s','g',' ',' '),	HB_TAG('S','G','O',' ')},	/* Sango */
+  {HB_TAG('s','h',' ',' '),	HB_TAG('B','O','S',' ')},	/* Serbo-Croatian [macrolanguage] -> Bosnian */
+  {HB_TAG('s','h',' ',' '),	HB_TAG('H','R','V',' ')},	/* Serbo-Croatian [macrolanguage] -> Croatian */
+  {HB_TAG('s','h',' ',' '),	HB_TAG('S','R','B',' ')},	/* Serbo-Croatian [macrolanguage] -> Serbian */
+  {HB_TAG('s','i',' ',' '),	HB_TAG('S','N','H',' ')},	/* Sinhala (Sinhalese) */
+  {HB_TAG('s','k',' ',' '),	HB_TAG('S','K','Y',' ')},	/* Slovak */
+  {HB_TAG('s','l',' ',' '),	HB_TAG('S','L','V',' ')},	/* Slovenian */
+  {HB_TAG('s','m',' ',' '),	HB_TAG('S','M','O',' ')},	/* Samoan */
+  {HB_TAG('s','n',' ',' '),	HB_TAG('S','N','A','0')},	/* Shona */
+  {HB_TAG('s','o',' ',' '),	HB_TAG('S','M','L',' ')},	/* Somali */
+  {HB_TAG('s','q',' ',' '),	HB_TAG('S','Q','I',' ')},	/* Albanian [macrolanguage] */
+  {HB_TAG('s','r',' ',' '),	HB_TAG('S','R','B',' ')},	/* Serbian */
+  {HB_TAG('s','s',' ',' '),	HB_TAG('S','W','Z',' ')},	/* Swati */
+  {HB_TAG('s','t',' ',' '),	HB_TAG('S','O','T',' ')},	/* Southern Sotho */
+  {HB_TAG('s','u',' ',' '),	HB_TAG('S','U','N',' ')},	/* Sundanese */
+  {HB_TAG('s','v',' ',' '),	HB_TAG('S','V','E',' ')},	/* Swedish */
+  {HB_TAG('s','w',' ',' '),	HB_TAG('S','W','K',' ')},	/* Swahili [macrolanguage] */
+  {HB_TAG('t','a',' ',' '),	HB_TAG('T','A','M',' ')},	/* Tamil */
+  {HB_TAG('t','e',' ',' '),	HB_TAG('T','E','L',' ')},	/* Telugu */
+  {HB_TAG('t','g',' ',' '),	HB_TAG('T','A','J',' ')},	/* Tajik -> Tajiki */
+  {HB_TAG('t','h',' ',' '),	HB_TAG('T','H','A',' ')},	/* Thai */
+  {HB_TAG('t','i',' ',' '),	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
+  {HB_TAG('t','k',' ',' '),	HB_TAG('T','K','M',' ')},	/* Turkmen */
+  {HB_TAG('t','l',' ',' '),	HB_TAG('T','G','L',' ')},	/* Tagalog */
+  {HB_TAG('t','n',' ',' '),	HB_TAG('T','N','A',' ')},	/* Tswana */
+  {HB_TAG('t','o',' ',' '),	HB_TAG('T','G','N',' ')},	/* Tonga (Tonga Islands) -> Tongan */
+  {HB_TAG('t','r',' ',' '),	HB_TAG('T','R','K',' ')},	/* Turkish */
+  {HB_TAG('t','s',' ',' '),	HB_TAG('T','S','G',' ')},	/* Tsonga */
+  {HB_TAG('t','t',' ',' '),	HB_TAG('T','A','T',' ')},	/* Tatar */
+  {HB_TAG('t','w',' ',' '),	HB_TAG('T','W','I',' ')},	/* Twi */
+  {HB_TAG('t','w',' ',' '),	HB_TAG('A','K','A',' ')},	/* Twi -> Akan */
+  {HB_TAG('t','y',' ',' '),	HB_TAG('T','H','T',' ')},	/* Tahitian */
+  {HB_TAG('u','g',' ',' '),	HB_TAG('U','Y','G',' ')},	/* Uyghur */
+  {HB_TAG('u','k',' ',' '),	HB_TAG('U','K','R',' ')},	/* Ukrainian */
+  {HB_TAG('u','r',' ',' '),	HB_TAG('U','R','D',' ')},	/* Urdu */
+  {HB_TAG('u','z',' ',' '),	HB_TAG('U','Z','B',' ')},	/* Uzbek [macrolanguage] */
+  {HB_TAG('v','e',' ',' '),	HB_TAG('V','E','N',' ')},	/* Venda */
+  {HB_TAG('v','i',' ',' '),	HB_TAG('V','I','T',' ')},	/* Vietnamese */
+  {HB_TAG('v','o',' ',' '),	HB_TAG('V','O','L',' ')},	/* Volapük */
+  {HB_TAG('w','a',' ',' '),	HB_TAG('W','L','N',' ')},	/* Walloon */
+  {HB_TAG('w','o',' ',' '),	HB_TAG('W','L','F',' ')},	/* Wolof */
+  {HB_TAG('x','h',' ',' '),	HB_TAG('X','H','S',' ')},	/* Xhosa */
+  {HB_TAG('y','i',' ',' '),	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
+  {HB_TAG('y','o',' ',' '),	HB_TAG('Y','B','A',' ')},	/* Yoruba */
+  {HB_TAG('z','a',' ',' '),	HB_TAG('Z','H','A',' ')},	/* Zhuang [macrolanguage] */
+  {HB_TAG('z','h',' ',' '),	HB_TAG('Z','H','S',' ')},	/* Chinese, Simplified [macrolanguage] */
+  {HB_TAG('z','u',' ',' '),	HB_TAG('Z','U','L',' ')},	/* Zulu */
 };
 
+#ifndef HB_NO_LANGUAGE_LONG
+static const LangTag ot_languages3[] = {
+  {HB_TAG('a','a','e',' '),	HB_TAG('S','Q','I',' ')},	/* Arbëreshë Albanian -> Albanian */
+  {HB_TAG('a','a','o',' '),	HB_TAG('A','R','A',' ')},	/* Algerian Saharan Arabic -> Arabic */
+  {HB_TAG('a','a','t',' '),	HB_TAG('S','Q','I',' ')},	/* Arvanitika Albanian -> Albanian */
+  {HB_TAG('a','b','a',' '),	HB_TAG_NONE	       },	/* Abé != Abaza */
+  {HB_TAG('a','b','h',' '),	HB_TAG('A','R','A',' ')},	/* Tajiki Arabic -> Arabic */
+  {HB_TAG('a','b','q',' '),	HB_TAG('A','B','A',' ')},	/* Abaza */
+  {HB_TAG('a','b','s',' '),	HB_TAG('C','P','P',' ')},	/* Ambonese Malay -> Creoles */
+  {HB_TAG('a','b','v',' '),	HB_TAG('A','R','A',' ')},	/* Baharna Arabic -> Arabic */
+  {HB_TAG('a','c','f',' '),	HB_TAG('F','A','N',' ')},	/* Saint Lucian Creole French -> French Antillean */
+  {HB_TAG('a','c','f',' '),	HB_TAG('C','P','P',' ')},	/* Saint Lucian Creole French -> Creoles */
+/*{HB_TAG('a','c','h',' '),	HB_TAG('A','C','H',' ')},*/	/* Acoli -> Acholi */
+  {HB_TAG('a','c','m',' '),	HB_TAG('A','R','A',' ')},	/* Mesopotamian Arabic -> Arabic */
+  {HB_TAG('a','c','q',' '),	HB_TAG('A','R','A',' ')},	/* Ta'izzi-Adeni Arabic -> Arabic */
+  {HB_TAG('a','c','r',' '),	HB_TAG('A','C','R',' ')},	/* Achi */
+  {HB_TAG('a','c','r',' '),	HB_TAG('M','Y','N',' ')},	/* Achi -> Mayan */
+  {HB_TAG('a','c','w',' '),	HB_TAG('A','R','A',' ')},	/* Hijazi Arabic -> Arabic */
+  {HB_TAG('a','c','x',' '),	HB_TAG('A','R','A',' ')},	/* Omani Arabic -> Arabic */
+  {HB_TAG('a','c','y',' '),	HB_TAG('A','R','A',' ')},	/* Cypriot Arabic -> Arabic */
+  {HB_TAG('a','d','a',' '),	HB_TAG('D','N','G',' ')},	/* Adangme -> Dangme */
+  {HB_TAG('a','d','f',' '),	HB_TAG('A','R','A',' ')},	/* Dhofari Arabic -> Arabic */
+  {HB_TAG('a','d','p',' '),	HB_TAG('D','Z','N',' ')},	/* Adap (retired code) -> Dzongkha */
+/*{HB_TAG('a','d','y',' '),	HB_TAG('A','D','Y',' ')},*/	/* Adyghe */
+  {HB_TAG('a','e','b',' '),	HB_TAG('A','R','A',' ')},	/* Tunisian Arabic -> Arabic */
+  {HB_TAG('a','e','c',' '),	HB_TAG('A','R','A',' ')},	/* Saidi Arabic -> Arabic */
+  {HB_TAG('a','f','b',' '),	HB_TAG('A','R','A',' ')},	/* Gulf Arabic -> Arabic */
+  {HB_TAG('a','f','k',' '),	HB_TAG_NONE	       },	/* Nanubae != Afrikaans */
+  {HB_TAG('a','f','s',' '),	HB_TAG('C','P','P',' ')},	/* Afro-Seminole Creole -> Creoles */
+  {HB_TAG('a','g','u',' '),	HB_TAG('M','Y','N',' ')},	/* Aguacateco -> Mayan */
+  {HB_TAG('a','g','w',' '),	HB_TAG_NONE	       },	/* Kahua != Agaw */
+  {HB_TAG('a','h','g',' '),	HB_TAG('A','G','W',' ')},	/* Qimant -> Agaw */
+  {HB_TAG('a','h','t',' '),	HB_TAG('A','T','H',' ')},	/* Ahtena -> Athapaskan */
+  {HB_TAG('a','i','g',' '),	HB_TAG('C','P','P',' ')},	/* Antigua and Barbuda Creole English -> Creoles */
+  {HB_TAG('a','i','i',' '),	HB_TAG('S','W','A',' ')},	/* Assyrian Neo-Aramaic -> Swadaya Aramaic */
+  {HB_TAG('a','i','i',' '),	HB_TAG('S','Y','R',' ')},	/* Assyrian Neo-Aramaic -> Syriac */
+/*{HB_TAG('a','i','o',' '),	HB_TAG('A','I','O',' ')},*/	/* Aiton */
+  {HB_TAG('a','i','w',' '),	HB_TAG('A','R','I',' ')},	/* Aari */
+  {HB_TAG('a','j','p',' '),	HB_TAG('A','R','A',' ')},	/* South Levantine Arabic -> Arabic */
+  {HB_TAG('a','j','t',' '),	HB_TAG('A','R','A',' ')},	/* Judeo-Tunisian Arabic (retired code) -> Arabic */
+  {HB_TAG('a','k','b',' '),	HB_TAG('A','K','B',' ')},	/* Batak Angkola */
+  {HB_TAG('a','k','b',' '),	HB_TAG('B','T','K',' ')},	/* Batak Angkola -> Batak */
+  {HB_TAG('a','l','n',' '),	HB_TAG('S','Q','I',' ')},	/* Gheg Albanian -> Albanian */
+  {HB_TAG('a','l','s',' '),	HB_TAG('S','Q','I',' ')},	/* Tosk Albanian -> Albanian */
+/*{HB_TAG('a','l','t',' '),	HB_TAG('A','L','T',' ')},*/	/* Southern Altai -> Altai */
+  {HB_TAG('a','m','f',' '),	HB_TAG('H','B','N',' ')},	/* Hamer-Banna -> Hammer-Banna */
+  {HB_TAG('a','m','w',' '),	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic -> Syriac */
+/*{HB_TAG('a','n','g',' '),	HB_TAG('A','N','G',' ')},*/	/* Old English (ca. 450-1100) -> Anglo-Saxon */
+  {HB_TAG('a','o','a',' '),	HB_TAG('C','P','P',' ')},	/* Angolar -> Creoles */
+  {HB_TAG('a','p','a',' '),	HB_TAG('A','T','H',' ')},	/* Apache [collection] -> Athapaskan */
+  {HB_TAG('a','p','c',' '),	HB_TAG('A','R','A',' ')},	/* North Levantine Arabic -> Arabic */
+  {HB_TAG('a','p','d',' '),	HB_TAG('A','R','A',' ')},	/* Sudanese Arabic -> Arabic */
+  {HB_TAG('a','p','j',' '),	HB_TAG('A','T','H',' ')},	/* Jicarilla Apache -> Athapaskan */
+  {HB_TAG('a','p','k',' '),	HB_TAG('A','T','H',' ')},	/* Kiowa Apache -> Athapaskan */
+  {HB_TAG('a','p','l',' '),	HB_TAG('A','T','H',' ')},	/* Lipan Apache -> Athapaskan */
+  {HB_TAG('a','p','m',' '),	HB_TAG('A','T','H',' ')},	/* Mescalero-Chiricahua Apache -> Athapaskan */
+  {HB_TAG('a','p','w',' '),	HB_TAG('A','T','H',' ')},	/* Western Apache -> Athapaskan */
+  {HB_TAG('a','r','b',' '),	HB_TAG('A','R','A',' ')},	/* Standard Arabic -> Arabic */
+  {HB_TAG('a','r','i',' '),	HB_TAG_NONE	       },	/* Arikara != Aari */
+  {HB_TAG('a','r','k',' '),	HB_TAG_NONE	       },	/* Arikapú != Rakhine */
+  {HB_TAG('a','r','n',' '),	HB_TAG('M','A','P',' ')},	/* Mapudungun */
+  {HB_TAG('a','r','q',' '),	HB_TAG('A','R','A',' ')},	/* Algerian Arabic -> Arabic */
+  {HB_TAG('a','r','s',' '),	HB_TAG('A','R','A',' ')},	/* Najdi Arabic -> Arabic */
+  {HB_TAG('a','r','y',' '),	HB_TAG('M','O','R',' ')},	/* Moroccan Arabic -> Moroccan */
+  {HB_TAG('a','r','y',' '),	HB_TAG('A','R','A',' ')},	/* Moroccan Arabic -> Arabic */
+  {HB_TAG('a','r','z',' '),	HB_TAG('A','R','A',' ')},	/* Egyptian Arabic -> Arabic */
+/*{HB_TAG('a','s','t',' '),	HB_TAG('A','S','T',' ')},*/	/* Asturian */
+/*{HB_TAG('a','t','h',' '),	HB_TAG('A','T','H',' ')},*/	/* Athapascan [collection] -> Athapaskan */
+  {HB_TAG('a','t','j',' '),	HB_TAG('R','C','R',' ')},	/* Atikamekw -> R-Cree */
+  {HB_TAG('a','t','v',' '),	HB_TAG('A','L','T',' ')},	/* Northern Altai -> Altai */
+  {HB_TAG('a','u','j',' '),	HB_TAG('B','B','R',' ')},	/* Awjilah -> Berber */
+  {HB_TAG('a','u','z',' '),	HB_TAG('A','R','A',' ')},	/* Uzbeki Arabic -> Arabic */
+  {HB_TAG('a','v','l',' '),	HB_TAG('A','R','A',' ')},	/* Eastern Egyptian Bedawi Arabic -> Arabic */
+/*{HB_TAG('a','v','n',' '),	HB_TAG('A','V','N',' ')},*/	/* Avatime */
+/*{HB_TAG('a','w','a',' '),	HB_TAG('A','W','A',' ')},*/	/* Awadhi */
+  {HB_TAG('a','y','c',' '),	HB_TAG('A','Y','M',' ')},	/* Southern Aymara -> Aymara */
+  {HB_TAG('a','y','h',' '),	HB_TAG('A','R','A',' ')},	/* Hadrami Arabic -> Arabic */
+  {HB_TAG('a','y','l',' '),	HB_TAG('A','R','A',' ')},	/* Libyan Arabic -> Arabic */
+  {HB_TAG('a','y','n',' '),	HB_TAG('A','R','A',' ')},	/* Sanaani Arabic -> Arabic */
+  {HB_TAG('a','y','p',' '),	HB_TAG('A','R','A',' ')},	/* North Mesopotamian Arabic -> Arabic */
+  {HB_TAG('a','y','r',' '),	HB_TAG('A','Y','M',' ')},	/* Central Aymara -> Aymara */
+  {HB_TAG('a','z','b',' '),	HB_TAG('A','Z','B',' ')},	/* South Azerbaijani -> Torki */
+  {HB_TAG('a','z','b',' '),	HB_TAG('A','Z','E',' ')},	/* South Azerbaijani -> Azerbaijani */
+  {HB_TAG('a','z','d',' '),	HB_TAG('N','A','H',' ')},	/* Eastern Durango Nahuatl -> Nahuatl */
+  {HB_TAG('a','z','j',' '),	HB_TAG('A','Z','E',' ')},	/* North Azerbaijani -> Azerbaijani */
+  {HB_TAG('a','z','n',' '),	HB_TAG('N','A','H',' ')},	/* Western Durango Nahuatl -> Nahuatl */
+  {HB_TAG('a','z','z',' '),	HB_TAG('N','A','H',' ')},	/* Highland Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('b','a','d',' '),	HB_TAG('B','A','D','0')},	/* Banda [collection] */
+  {HB_TAG('b','a','g',' '),	HB_TAG_NONE	       },	/* Tuki != Baghelkhandi */
+  {HB_TAG('b','a','h',' '),	HB_TAG('C','P','P',' ')},	/* Bahamas Creole English -> Creoles */
+  {HB_TAG('b','a','i',' '),	HB_TAG('B','M','L',' ')},	/* Bamileke [collection] */
+  {HB_TAG('b','a','l',' '),	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolanguage] */
+/*{HB_TAG('b','a','n',' '),	HB_TAG('B','A','N',' ')},*/	/* Balinese */
+/*{HB_TAG('b','a','r',' '),	HB_TAG('B','A','R',' ')},*/	/* Bavarian */
+  {HB_TAG('b','a','u',' '),	HB_TAG_NONE	       },	/* Bada (Nigeria) != Baulé */
+  {HB_TAG('b','b','c',' '),	HB_TAG('B','B','C',' ')},	/* Batak Toba */
+  {HB_TAG('b','b','c',' '),	HB_TAG('B','T','K',' ')},	/* Batak Toba -> Batak */
+  {HB_TAG('b','b','j',' '),	HB_TAG('B','M','L',' ')},	/* Ghomálá' -> Bamileke */
+  {HB_TAG('b','b','p',' '),	HB_TAG('B','A','D','0')},	/* West Central Banda -> Banda */
+  {HB_TAG('b','b','r',' '),	HB_TAG_NONE	       },	/* Girawa != Berber */
+  {HB_TAG('b','b','z',' '),	HB_TAG('A','R','A',' ')},	/* Babalia Creole Arabic (retired code) -> Arabic */
+  {HB_TAG('b','c','c',' '),	HB_TAG('B','L','I',' ')},	/* Southern Balochi -> Baluchi */
+  {HB_TAG('b','c','h',' '),	HB_TAG_NONE	       },	/* Bariai != Bench */
+  {HB_TAG('b','c','i',' '),	HB_TAG('B','A','U',' ')},	/* Baoulé -> Baulé */
+  {HB_TAG('b','c','l',' '),	HB_TAG('B','I','K',' ')},	/* Central Bikol -> Bikol */
+  {HB_TAG('b','c','q',' '),	HB_TAG('B','C','H',' ')},	/* Bench */
+  {HB_TAG('b','c','r',' '),	HB_TAG('A','T','H',' ')},	/* Babine -> Athapaskan */
+/*{HB_TAG('b','d','y',' '),	HB_TAG('B','D','Y',' ')},*/	/* Bandjalang */
+  {HB_TAG('b','e','a',' '),	HB_TAG('A','T','H',' ')},	/* Beaver -> Athapaskan */
+  {HB_TAG('b','e','b',' '),	HB_TAG('B','T','I',' ')},	/* Bebele -> Beti */
+/*{HB_TAG('b','e','m',' '),	HB_TAG('B','E','M',' ')},*/	/* Bemba (Zambia) */
+  {HB_TAG('b','e','r',' '),	HB_TAG('B','B','R',' ')},	/* Berber [collection] */
+  {HB_TAG('b','e','w',' '),	HB_TAG('C','P','P',' ')},	/* Betawi -> Creoles */
+  {HB_TAG('b','f','l',' '),	HB_TAG('B','A','D','0')},	/* Banda-Ndélé -> Banda */
+  {HB_TAG('b','f','q',' '),	HB_TAG('B','A','D',' ')},	/* Badaga */
+  {HB_TAG('b','f','t',' '),	HB_TAG('B','L','T',' ')},	/* Balti */
+  {HB_TAG('b','f','u',' '),	HB_TAG('L','A','H',' ')},	/* Gahri -> Lahuli */
+  {HB_TAG('b','f','y',' '),	HB_TAG('B','A','G',' ')},	/* Bagheli -> Baghelkhandi */
+/*{HB_TAG('b','g','c',' '),	HB_TAG('B','G','C',' ')},*/	/* Haryanvi */
+  {HB_TAG('b','g','n',' '),	HB_TAG('B','L','I',' ')},	/* Western Balochi -> Baluchi */
+  {HB_TAG('b','g','p',' '),	HB_TAG('B','L','I',' ')},	/* Eastern Balochi -> Baluchi */
+  {HB_TAG('b','g','q',' '),	HB_TAG('B','G','Q',' ')},	/* Bagri */
+  {HB_TAG('b','g','q',' '),	HB_TAG('R','A','J',' ')},	/* Bagri -> Rajasthani */
+  {HB_TAG('b','g','r',' '),	HB_TAG('Q','I','N',' ')},	/* Bawm Chin -> Chin */
+  {HB_TAG('b','h','b',' '),	HB_TAG('B','H','I',' ')},	/* Bhili */
+/*{HB_TAG('b','h','i',' '),	HB_TAG('B','H','I',' ')},*/	/* Bhilali -> Bhili */
+  {HB_TAG('b','h','k',' '),	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) -> Bikol */
+/*{HB_TAG('b','h','o',' '),	HB_TAG('B','H','O',' ')},*/	/* Bhojpuri */
+  {HB_TAG('b','h','r',' '),	HB_TAG('M','L','G',' ')},	/* Bara Malagasy -> Malagasy */
+/*{HB_TAG('b','i','k',' '),	HB_TAG('B','I','K',' ')},*/	/* Bikol [macrolanguage] */
+  {HB_TAG('b','i','l',' '),	HB_TAG_NONE	       },	/* Bile != Bilen */
+  {HB_TAG('b','i','n',' '),	HB_TAG('E','D','O',' ')},	/* Edo */
+  {HB_TAG('b','i','u',' '),	HB_TAG('Q','I','N',' ')},	/* Biete -> Chin */
+/*{HB_TAG('b','j','j',' '),	HB_TAG('B','J','J',' ')},*/	/* Kanauji */
+  {HB_TAG('b','j','n',' '),	HB_TAG('M','L','Y',' ')},	/* Banjar -> Malay */
+  {HB_TAG('b','j','o',' '),	HB_TAG('B','A','D','0')},	/* Mid-Southern Banda -> Banda */
+  {HB_TAG('b','j','q',' '),	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */
+  {HB_TAG('b','j','s',' '),	HB_TAG('C','P','P',' ')},	/* Bajan -> Creoles */
+  {HB_TAG('b','j','t',' '),	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja -> Balante */
+  {HB_TAG('b','k','f',' '),	HB_TAG_NONE	       },	/* Beeke != Blackfoot */
+  {HB_TAG('b','k','o',' '),	HB_TAG('B','M','L',' ')},	/* Kwa' -> Bamileke */
+  {HB_TAG('b','l','a',' '),	HB_TAG('B','K','F',' ')},	/* Siksika -> Blackfoot */
+  {HB_TAG('b','l','e',' '),	HB_TAG('B','L','N',' ')},	/* Balanta-Kentohe -> Balante */
+  {HB_TAG('b','l','g',' '),	HB_TAG('I','B','A',' ')},	/* Balau (retired code) -> Iban */
+  {HB_TAG('b','l','i',' '),	HB_TAG_NONE	       },	/* Bolia != Baluchi */
+  {HB_TAG('b','l','k',' '),	HB_TAG('B','L','K',' ')},	/* Pa’o Karen */
+  {HB_TAG('b','l','k',' '),	HB_TAG('K','R','N',' ')},	/* Pa'o Karen -> Karen */
+  {HB_TAG('b','l','n',' '),	HB_TAG('B','I','K',' ')},	/* Southern Catanduanes Bikol -> Bikol */
+  {HB_TAG('b','l','t',' '),	HB_TAG_NONE	       },	/* Tai Dam != Balti */
+  {HB_TAG('b','m','b',' '),	HB_TAG_NONE	       },	/* Bembe != Bambara (Bamanankan) */
+  {HB_TAG('b','m','l',' '),	HB_TAG_NONE	       },	/* Bomboli != Bamileke */
+  {HB_TAG('b','m','m',' '),	HB_TAG('M','L','G',' ')},	/* Northern Betsimisaraka Malagasy -> Malagasy */
+  {HB_TAG('b','p','d',' '),	HB_TAG('B','A','D','0')},	/* Banda-Banda -> Banda */
+  {HB_TAG('b','p','l',' '),	HB_TAG('C','P','P',' ')},	/* Broome Pearling Lugger Pidgin -> Creoles */
+  {HB_TAG('b','p','q',' '),	HB_TAG('C','P','P',' ')},	/* Banda Malay -> Creoles */
+/*{HB_TAG('b','p','y',' '),	HB_TAG('B','P','Y',' ')},*/	/* Bishnupriya -> Bishnupriya Manipuri */
+  {HB_TAG('b','q','i',' '),	HB_TAG('L','R','C',' ')},	/* Bakhtiari -> Luri */
+  {HB_TAG('b','q','k',' '),	HB_TAG('B','A','D','0')},	/* Banda-Mbrès -> Banda */
+  {HB_TAG('b','r','a',' '),	HB_TAG('B','R','I',' ')},	/* Braj -> Braj Bhasha */
+  {HB_TAG('b','r','c',' '),	HB_TAG('C','P','P',' ')},	/* Berbice Creole Dutch -> Creoles */
+/*{HB_TAG('b','r','h',' '),	HB_TAG('B','R','H',' ')},*/	/* Brahui */
+  {HB_TAG('b','r','i',' '),	HB_TAG_NONE	       },	/* Mokpwe != Braj Bhasha */
+  {HB_TAG('b','r','m',' '),	HB_TAG_NONE	       },	/* Barambu != Burmese */
+/*{HB_TAG('b','r','x',' '),	HB_TAG('B','R','X',' ')},*/	/* Bodo (India) */
+  {HB_TAG('b','s','h',' '),	HB_TAG_NONE	       },	/* Kati != Bashkir */
+/*{HB_TAG('b','s','k',' '),	HB_TAG('B','S','K',' ')},*/	/* Burushaski */
+  {HB_TAG('b','t','b',' '),	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) (retired code) */
+  {HB_TAG('b','t','d',' '),	HB_TAG('B','T','D',' ')},	/* Batak Dairi (Pakpak) */
+  {HB_TAG('b','t','d',' '),	HB_TAG('B','T','K',' ')},	/* Batak Dairi -> Batak */
+  {HB_TAG('b','t','i',' '),	HB_TAG_NONE	       },	/* Burate != Beti */
+  {HB_TAG('b','t','j',' '),	HB_TAG('M','L','Y',' ')},	/* Bacanese Malay -> Malay */
+/*{HB_TAG('b','t','k',' '),	HB_TAG('B','T','K',' ')},*/	/* Batak [collection] */
+  {HB_TAG('b','t','m',' '),	HB_TAG('B','T','M',' ')},	/* Batak Mandailing */
+  {HB_TAG('b','t','m',' '),	HB_TAG('B','T','K',' ')},	/* Batak Mandailing -> Batak */
+  {HB_TAG('b','t','o',' '),	HB_TAG('B','I','K',' ')},	/* Rinconada Bikol -> Bikol */
+  {HB_TAG('b','t','s',' '),	HB_TAG('B','T','S',' ')},	/* Batak Simalungun */
+  {HB_TAG('b','t','s',' '),	HB_TAG('B','T','K',' ')},	/* Batak Simalungun -> Batak */
+  {HB_TAG('b','t','x',' '),	HB_TAG('B','T','X',' ')},	/* Batak Karo */
+  {HB_TAG('b','t','x',' '),	HB_TAG('B','T','K',' ')},	/* Batak Karo -> Batak */
+  {HB_TAG('b','t','z',' '),	HB_TAG('B','T','Z',' ')},	/* Batak Alas-Kluet */
+  {HB_TAG('b','t','z',' '),	HB_TAG('B','T','K',' ')},	/* Batak Alas-Kluet -> Batak */
+/*{HB_TAG('b','u','g',' '),	HB_TAG('B','U','G',' ')},*/	/* Buginese -> Bugis */
+  {HB_TAG('b','u','m',' '),	HB_TAG('B','T','I',' ')},	/* Bulu (Cameroon) -> Beti */
+  {HB_TAG('b','v','e',' '),	HB_TAG('M','L','Y',' ')},	/* Berau Malay -> Malay */
+  {HB_TAG('b','v','u',' '),	HB_TAG('M','L','Y',' ')},	/* Bukit Malay -> Malay */
+  {HB_TAG('b','w','e',' '),	HB_TAG('K','R','N',' ')},	/* Bwe Karen -> Karen */
+  {HB_TAG('b','x','k',' '),	HB_TAG('L','U','H',' ')},	/* Bukusu -> Luyia */
+  {HB_TAG('b','x','o',' '),	HB_TAG('C','P','P',' ')},	/* Barikanchi -> Creoles */
+  {HB_TAG('b','x','p',' '),	HB_TAG('B','T','I',' ')},	/* Bebil -> Beti */
+  {HB_TAG('b','x','r',' '),	HB_TAG('R','B','U',' ')},	/* Russia Buriat -> Russian Buriat */
+  {HB_TAG('b','y','n',' '),	HB_TAG('B','I','L',' ')},	/* Bilin -> Bilen */
+  {HB_TAG('b','y','v',' '),	HB_TAG('B','Y','V',' ')},	/* Medumba */
+  {HB_TAG('b','y','v',' '),	HB_TAG('B','M','L',' ')},	/* Medumba -> Bamileke */
+  {HB_TAG('b','z','c',' '),	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy -> Malagasy */
+  {HB_TAG('b','z','j',' '),	HB_TAG('C','P','P',' ')},	/* Belize Kriol English -> Creoles */
+  {HB_TAG('b','z','k',' '),	HB_TAG('C','P','P',' ')},	/* Nicaragua Creole English -> Creoles */
+  {HB_TAG('c','a','a',' '),	HB_TAG('M','Y','N',' ')},	/* Chortí -> Mayan */
+  {HB_TAG('c','a','c',' '),	HB_TAG('M','Y','N',' ')},	/* Chuj -> Mayan */
+  {HB_TAG('c','a','f',' '),	HB_TAG('C','R','R',' ')},	/* Southern Carrier -> Carrier */
+  {HB_TAG('c','a','f',' '),	HB_TAG('A','T','H',' ')},	/* Southern Carrier -> Athapaskan */
+  {HB_TAG('c','a','k',' '),	HB_TAG('C','A','K',' ')},	/* Kaqchikel */
+  {HB_TAG('c','a','k',' '),	HB_TAG('M','Y','N',' ')},	/* Kaqchikel -> Mayan */
+  {HB_TAG('c','b','k',' '),	HB_TAG('C','B','K',' ')},	/* Chavacano -> Zamboanga Chavacano */
+  {HB_TAG('c','b','k',' '),	HB_TAG('C','P','P',' ')},	/* Chavacano -> Creoles */
+  {HB_TAG('c','b','l',' '),	HB_TAG('Q','I','N',' ')},	/* Bualkhaw Chin -> Chin */
+  {HB_TAG('c','c','l',' '),	HB_TAG('C','P','P',' ')},	/* Cutchi-Swahili -> Creoles */
+  {HB_TAG('c','c','m',' '),	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Malay -> Creoles */
+  {HB_TAG('c','c','o',' '),	HB_TAG('C','C','H','N')},	/* Comaltepec Chinantec -> Chinantec */
+  {HB_TAG('c','c','q',' '),	HB_TAG('A','R','K',' ')},	/* Chaungtha (retired code) -> Rakhine */
+  {HB_TAG('c','d','o',' '),	HB_TAG('Z','H','S',' ')},	/* Min Dong Chinese -> Chinese, Simplified */
+/*{HB_TAG('c','e','b',' '),	HB_TAG('C','E','B',' ')},*/	/* Cebuano */
+  {HB_TAG('c','e','k',' '),	HB_TAG('Q','I','N',' ')},	/* Eastern Khumi Chin -> Chin */
+  {HB_TAG('c','e','y',' '),	HB_TAG('Q','I','N',' ')},	/* Ekai Chin -> Chin */
+  {HB_TAG('c','f','m',' '),	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) */
+  {HB_TAG('c','f','m',' '),	HB_TAG('Q','I','N',' ')},	/* Falam Chin -> Chin */
+/*{HB_TAG('c','g','g',' '),	HB_TAG('C','G','G',' ')},*/	/* Chiga */
+  {HB_TAG('c','h','f',' '),	HB_TAG('M','Y','N',' ')},	/* Tabasco Chontal -> Mayan */
+  {HB_TAG('c','h','g',' '),	HB_TAG_NONE	       },	/* Chagatai != Chaha Gurage */
+  {HB_TAG('c','h','h',' '),	HB_TAG_NONE	       },	/* Chinook != Chattisgarhi */
+  {HB_TAG('c','h','j',' '),	HB_TAG('C','C','H','N')},	/* Ojitlán Chinantec -> Chinantec */
+  {HB_TAG('c','h','k',' '),	HB_TAG('C','H','K','0')},	/* Chuukese */
+  {HB_TAG('c','h','m',' '),	HB_TAG('H','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> High Mari */
+  {HB_TAG('c','h','m',' '),	HB_TAG('L','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> Low Mari */
+  {HB_TAG('c','h','n',' '),	HB_TAG('C','P','P',' ')},	/* Chinook jargon -> Creoles */
+/*{HB_TAG('c','h','o',' '),	HB_TAG('C','H','O',' ')},*/	/* Choctaw */
+  {HB_TAG('c','h','p',' '),	HB_TAG('C','H','P',' ')},	/* Chipewyan */
+  {HB_TAG('c','h','p',' '),	HB_TAG('S','A','Y',' ')},	/* Chipewyan -> Sayisi */
+  {HB_TAG('c','h','p',' '),	HB_TAG('A','T','H',' ')},	/* Chipewyan -> Athapaskan */
+  {HB_TAG('c','h','q',' '),	HB_TAG('C','C','H','N')},	/* Quiotepec Chinantec -> Chinantec */
+/*{HB_TAG('c','h','r',' '),	HB_TAG('C','H','R',' ')},*/	/* Cherokee */
+/*{HB_TAG('c','h','y',' '),	HB_TAG('C','H','Y',' ')},*/	/* Cheyenne */
+  {HB_TAG('c','h','z',' '),	HB_TAG('C','C','H','N')},	/* Ozumacín Chinantec -> Chinantec */
+  {HB_TAG('c','i','w',' '),	HB_TAG('O','J','B',' ')},	/* Chippewa -> Ojibway */
+/*{HB_TAG('c','j','a',' '),	HB_TAG('C','J','A',' ')},*/	/* Western Cham */
+/*{HB_TAG('c','j','m',' '),	HB_TAG('C','J','M',' ')},*/	/* Eastern Cham */
+  {HB_TAG('c','j','y',' '),	HB_TAG('Z','H','S',' ')},	/* Jinyu Chinese -> Chinese, Simplified */
+  {HB_TAG('c','k','a',' '),	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin (retired code) -> Chin */
+  {HB_TAG('c','k','b',' '),	HB_TAG('K','U','R',' ')},	/* Central Kurdish -> Kurdish */
+  {HB_TAG('c','k','n',' '),	HB_TAG('Q','I','N',' ')},	/* Kaang Chin -> Chin */
+  {HB_TAG('c','k','s',' '),	HB_TAG('C','P','P',' ')},	/* Tayo -> Creoles */
+  {HB_TAG('c','k','t',' '),	HB_TAG('C','H','K',' ')},	/* Chukot -> Chukchi */
+  {HB_TAG('c','k','z',' '),	HB_TAG('M','Y','N',' ')},	/* Cakchiquel-Quiché Mixed Language -> Mayan */
+  {HB_TAG('c','l','c',' '),	HB_TAG('A','T','H',' ')},	/* Chilcotin -> Athapaskan */
+  {HB_TAG('c','l','d',' '),	HB_TAG('S','Y','R',' ')},	/* Chaldean Neo-Aramaic -> Syriac */
+  {HB_TAG('c','l','e',' '),	HB_TAG('C','C','H','N')},	/* Lealao Chinantec -> Chinantec */
+  {HB_TAG('c','l','j',' '),	HB_TAG('Q','I','N',' ')},	/* Laitu Chin -> Chin */
+  {HB_TAG('c','l','t',' '),	HB_TAG('Q','I','N',' ')},	/* Lautu Chin -> Chin */
+  {HB_TAG('c','m','n',' '),	HB_TAG('Z','H','S',' ')},	/* Mandarin Chinese -> Chinese, Simplified */
+  {HB_TAG('c','m','r',' '),	HB_TAG('Q','I','N',' ')},	/* Mro-Khimi Chin -> Chin */
+  {HB_TAG('c','n','b',' '),	HB_TAG('Q','I','N',' ')},	/* Chinbon Chin -> Chin */
+  {HB_TAG('c','n','h',' '),	HB_TAG('Q','I','N',' ')},	/* Hakha Chin -> Chin */
+  {HB_TAG('c','n','k',' '),	HB_TAG('Q','I','N',' ')},	/* Khumi Chin -> Chin */
+  {HB_TAG('c','n','l',' '),	HB_TAG('C','C','H','N')},	/* Lalana Chinantec -> Chinantec */
+  {HB_TAG('c','n','p',' '),	HB_TAG('Z','H','S',' ')},	/* Northern Ping Chinese -> Chinese, Simplified */
+  {HB_TAG('c','n','r',' '),	HB_TAG('S','R','B',' ')},	/* Montenegrin -> Serbian */
+  {HB_TAG('c','n','t',' '),	HB_TAG('C','C','H','N')},	/* Tepetotutla Chinantec -> Chinantec */
+  {HB_TAG('c','n','u',' '),	HB_TAG('B','B','R',' ')},	/* Chenoua -> Berber */
+  {HB_TAG('c','n','w',' '),	HB_TAG('Q','I','N',' ')},	/* Ngawn Chin -> Chin */
+  {HB_TAG('c','o','a',' '),	HB_TAG('M','L','Y',' ')},	/* Cocos Islands Malay -> Malay */
+  {HB_TAG('c','o','b',' '),	HB_TAG('M','Y','N',' ')},	/* Chicomuceltec -> Mayan */
+/*{HB_TAG('c','o','p',' '),	HB_TAG('C','O','P',' ')},*/	/* Coptic */
+  {HB_TAG('c','o','q',' '),	HB_TAG('A','T','H',' ')},	/* Coquille -> Athapaskan */
+  {HB_TAG('c','p','a',' '),	HB_TAG('C','C','H','N')},	/* Palantla Chinantec -> Chinantec */
+  {HB_TAG('c','p','e',' '),	HB_TAG('C','P','P',' ')},	/* English-based creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','p','f',' '),	HB_TAG('C','P','P',' ')},	/* French-based creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','p','i',' '),	HB_TAG('C','P','P',' ')},	/* Chinese Pidgin English -> Creoles */
+/*{HB_TAG('c','p','p',' '),	HB_TAG('C','P','P',' ')},*/	/* Portuguese-based creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','p','x',' '),	HB_TAG('Z','H','S',' ')},	/* Pu-Xian Chinese -> Chinese, Simplified */
+  {HB_TAG('c','q','d',' '),	HB_TAG('H','M','N',' ')},	/* Chuanqiandian Cluster Miao -> Hmong */
+  {HB_TAG('c','q','u',' '),	HB_TAG('Q','U','H',' ')},	/* Chilean Quechua (retired code) -> Quechua (Bolivia) */
+  {HB_TAG('c','q','u',' '),	HB_TAG('Q','U','Z',' ')},	/* Chilean Quechua (retired code) -> Quechua */
+  {HB_TAG('c','r','h',' '),	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
+  {HB_TAG('c','r','i',' '),	HB_TAG('C','P','P',' ')},	/* Sãotomense -> Creoles */
+  {HB_TAG('c','r','j',' '),	HB_TAG('E','C','R',' ')},	/* Southern East Cree -> Eastern Cree */
+  {HB_TAG('c','r','j',' '),	HB_TAG('Y','C','R',' ')},	/* Southern East Cree -> Y-Cree */
+  {HB_TAG('c','r','j',' '),	HB_TAG('C','R','E',' ')},	/* Southern East Cree -> Cree */
+  {HB_TAG('c','r','k',' '),	HB_TAG('W','C','R',' ')},	/* Plains Cree -> West-Cree */
+  {HB_TAG('c','r','k',' '),	HB_TAG('Y','C','R',' ')},	/* Plains Cree -> Y-Cree */
+  {HB_TAG('c','r','k',' '),	HB_TAG('C','R','E',' ')},	/* Plains Cree -> Cree */
+  {HB_TAG('c','r','l',' '),	HB_TAG('E','C','R',' ')},	/* Northern East Cree -> Eastern Cree */
+  {HB_TAG('c','r','l',' '),	HB_TAG('Y','C','R',' ')},	/* Northern East Cree -> Y-Cree */
+  {HB_TAG('c','r','l',' '),	HB_TAG('C','R','E',' ')},	/* Northern East Cree -> Cree */
+  {HB_TAG('c','r','m',' '),	HB_TAG('M','C','R',' ')},	/* Moose Cree */
+  {HB_TAG('c','r','m',' '),	HB_TAG('L','C','R',' ')},	/* Moose Cree -> L-Cree */
+  {HB_TAG('c','r','m',' '),	HB_TAG('C','R','E',' ')},	/* Moose Cree -> Cree */
+  {HB_TAG('c','r','p',' '),	HB_TAG('C','P','P',' ')},	/* Creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','r','r',' '),	HB_TAG_NONE	       },	/* Carolina Algonquian != Carrier */
+  {HB_TAG('c','r','s',' '),	HB_TAG('C','P','P',' ')},	/* Seselwa Creole French -> Creoles */
+  {HB_TAG('c','r','t',' '),	HB_TAG_NONE	       },	/* Iyojwa'ja Chorote != Crimean Tatar */
+  {HB_TAG('c','r','x',' '),	HB_TAG('C','R','R',' ')},	/* Carrier */
+  {HB_TAG('c','r','x',' '),	HB_TAG('A','T','H',' ')},	/* Carrier -> Athapaskan */
+  {HB_TAG('c','s','a',' '),	HB_TAG('C','C','H','N')},	/* Chiltepec Chinantec -> Chinantec */
+/*{HB_TAG('c','s','b',' '),	HB_TAG('C','S','B',' ')},*/	/* Kashubian */
+  {HB_TAG('c','s','h',' '),	HB_TAG('Q','I','N',' ')},	/* Asho Chin -> Chin */
+  {HB_TAG('c','s','j',' '),	HB_TAG('Q','I','N',' ')},	/* Songlai Chin -> Chin */
+  {HB_TAG('c','s','l',' '),	HB_TAG_NONE	       },	/* Chinese Sign Language != Church Slavonic */
+  {HB_TAG('c','s','o',' '),	HB_TAG('C','C','H','N')},	/* Sochiapam Chinantec -> Chinantec */
+  {HB_TAG('c','s','p',' '),	HB_TAG('Z','H','S',' ')},	/* Southern Ping Chinese -> Chinese, Simplified */
+  {HB_TAG('c','s','v',' '),	HB_TAG('Q','I','N',' ')},	/* Sumtu Chin -> Chin */
+  {HB_TAG('c','s','w',' '),	HB_TAG('N','C','R',' ')},	/* Swampy Cree -> N-Cree */
+  {HB_TAG('c','s','w',' '),	HB_TAG('N','H','C',' ')},	/* Swampy Cree -> Norway House Cree */
+  {HB_TAG('c','s','w',' '),	HB_TAG('C','R','E',' ')},	/* Swampy Cree -> Cree */
+  {HB_TAG('c','s','y',' '),	HB_TAG('Q','I','N',' ')},	/* Siyin Chin -> Chin */
+  {HB_TAG('c','t','c',' '),	HB_TAG('A','T','H',' ')},	/* Chetco -> Athapaskan */
+  {HB_TAG('c','t','d',' '),	HB_TAG('Q','I','N',' ')},	/* Tedim Chin -> Chin */
+  {HB_TAG('c','t','e',' '),	HB_TAG('C','C','H','N')},	/* Tepinapa Chinantec -> Chinantec */
+/*{HB_TAG('c','t','g',' '),	HB_TAG('C','T','G',' ')},*/	/* Chittagonian */
+  {HB_TAG('c','t','h',' '),	HB_TAG('Q','I','N',' ')},	/* Thaiphum Chin -> Chin */
+  {HB_TAG('c','t','l',' '),	HB_TAG('C','C','H','N')},	/* Tlacoatzintepec Chinantec -> Chinantec */
+  {HB_TAG('c','t','s',' '),	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol -> Bikol */
+/*{HB_TAG('c','t','t',' '),	HB_TAG('C','T','T',' ')},*/	/* Wayanad Chetti */
+  {HB_TAG('c','t','u',' '),	HB_TAG('M','Y','N',' ')},	/* Chol -> Mayan */
+  {HB_TAG('c','u','c',' '),	HB_TAG('C','C','H','N')},	/* Usila Chinantec -> Chinantec */
+/*{HB_TAG('c','u','k',' '),	HB_TAG('C','U','K',' ')},*/	/* San Blas Kuna */
+  {HB_TAG('c','v','n',' '),	HB_TAG('C','C','H','N')},	/* Valle Nacional Chinantec -> Chinantec */
+  {HB_TAG('c','w','d',' '),	HB_TAG('D','C','R',' ')},	/* Woods Cree */
+  {HB_TAG('c','w','d',' '),	HB_TAG('T','C','R',' ')},	/* Woods Cree -> TH-Cree */
+  {HB_TAG('c','w','d',' '),	HB_TAG('C','R','E',' ')},	/* Woods Cree -> Cree */
+  {HB_TAG('c','z','h',' '),	HB_TAG('Z','H','S',' ')},	/* Huizhou Chinese -> Chinese, Simplified */
+  {HB_TAG('c','z','o',' '),	HB_TAG('Z','H','S',' ')},	/* Min Zhong Chinese -> Chinese, Simplified */
+  {HB_TAG('c','z','t',' '),	HB_TAG('Q','I','N',' ')},	/* Zotung Chin -> Chin */
+/*{HB_TAG('d','a','g',' '),	HB_TAG('D','A','G',' ')},*/	/* Dagbani */
+  {HB_TAG('d','a','o',' '),	HB_TAG('Q','I','N',' ')},	/* Daai Chin -> Chin */
+  {HB_TAG('d','a','p',' '),	HB_TAG('N','I','S',' ')},	/* Nisi (India) (retired code) */
+/*{HB_TAG('d','a','r',' '),	HB_TAG('D','A','R',' ')},*/	/* Dargwa */
+/*{HB_TAG('d','a','x',' '),	HB_TAG('D','A','X',' ')},*/	/* Dayi */
+  {HB_TAG('d','c','r',' '),	HB_TAG('C','P','P',' ')},	/* Negerhollands -> Creoles */
+  {HB_TAG('d','e','n',' '),	HB_TAG('S','L','A',' ')},	/* Slave (Athapascan) [macrolanguage] -> Slavey */
+  {HB_TAG('d','e','n',' '),	HB_TAG('A','T','H',' ')},	/* Slave (Athapascan) [macrolanguage] -> Athapaskan */
+  {HB_TAG('d','e','p',' '),	HB_TAG('C','P','P',' ')},	/* Pidgin Delaware -> Creoles */
+  {HB_TAG('d','g','o',' '),	HB_TAG('D','G','O',' ')},	/* Dogri (individual language) */
+  {HB_TAG('d','g','o',' '),	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) */
+  {HB_TAG('d','g','r',' '),	HB_TAG('A','T','H',' ')},	/* Dogrib -> Athapaskan */
+  {HB_TAG('d','h','d',' '),	HB_TAG('M','A','W',' ')},	/* Dhundari -> Marwari */
+/*{HB_TAG('d','h','g',' '),	HB_TAG('D','H','G',' ')},*/	/* Dhangu */
+  {HB_TAG('d','h','v',' '),	HB_TAG_NONE	       },	/* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */
+  {HB_TAG('d','i','b',' '),	HB_TAG('D','N','K',' ')},	/* South Central Dinka -> Dinka */
+  {HB_TAG('d','i','k',' '),	HB_TAG('D','N','K',' ')},	/* Southwestern Dinka -> Dinka */
+  {HB_TAG('d','i','n',' '),	HB_TAG('D','N','K',' ')},	/* Dinka [macrolanguage] */
+  {HB_TAG('d','i','p',' '),	HB_TAG('D','N','K',' ')},	/* Northeastern Dinka -> Dinka */
+  {HB_TAG('d','i','q',' '),	HB_TAG('D','I','Q',' ')},	/* Dimli */
+  {HB_TAG('d','i','q',' '),	HB_TAG('Z','Z','A',' ')},	/* Dimli -> Zazaki */
+  {HB_TAG('d','i','w',' '),	HB_TAG('D','N','K',' ')},	/* Northwestern Dinka -> Dinka */
+  {HB_TAG('d','j','e',' '),	HB_TAG('D','J','R',' ')},	/* Zarma */
+  {HB_TAG('d','j','k',' '),	HB_TAG('C','P','P',' ')},	/* Eastern Maroon Creole -> Creoles */
+  {HB_TAG('d','j','r',' '),	HB_TAG('D','J','R','0')},	/* Djambarrpuyngu */
+  {HB_TAG('d','k','s',' '),	HB_TAG('D','N','K',' ')},	/* Southeastern Dinka -> Dinka */
+  {HB_TAG('d','n','g',' '),	HB_TAG('D','U','N',' ')},	/* Dungan */
+/*{HB_TAG('d','n','j',' '),	HB_TAG('D','N','J',' ')},*/	/* Dan */
+  {HB_TAG('d','n','k',' '),	HB_TAG_NONE	       },	/* Dengka != Dinka */
+  {HB_TAG('d','o','i',' '),	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) [macrolanguage] */
+  {HB_TAG('d','r','h',' '),	HB_TAG('M','N','G',' ')},	/* Darkhat (retired code) -> Mongolian */
+  {HB_TAG('d','r','i',' '),	HB_TAG_NONE	       },	/* C'Lela != Dari */
+  {HB_TAG('d','r','w',' '),	HB_TAG('D','R','I',' ')},	/* Darwazi (retired code) -> Dari */
+  {HB_TAG('d','r','w',' '),	HB_TAG('F','A','R',' ')},	/* Darwazi (retired code) -> Persian */
+  {HB_TAG('d','s','b',' '),	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
+  {HB_TAG('d','t','y',' '),	HB_TAG('N','E','P',' ')},	/* Dotyali -> Nepali */
+/*{HB_TAG('d','u','j',' '),	HB_TAG('D','U','J',' ')},*/	/* Dhuwal (retired code) */
+  {HB_TAG('d','u','n',' '),	HB_TAG_NONE	       },	/* Dusun Deyah != Dungan */
+  {HB_TAG('d','u','p',' '),	HB_TAG('M','L','Y',' ')},	/* Duano -> Malay */
+  {HB_TAG('d','w','k',' '),	HB_TAG('K','U','I',' ')},	/* Dawik Kui -> Kui */
+  {HB_TAG('d','w','u',' '),	HB_TAG('D','U','J',' ')},	/* Dhuwal */
+  {HB_TAG('d','w','y',' '),	HB_TAG('D','U','J',' ')},	/* Dhuwaya -> Dhuwal */
+  {HB_TAG('d','y','u',' '),	HB_TAG('J','U','L',' ')},	/* Dyula -> Jula */
+  {HB_TAG('d','z','n',' '),	HB_TAG_NONE	       },	/* Dzando != Dzongkha */
+  {HB_TAG('e','c','r',' '),	HB_TAG_NONE	       },	/* Eteocretan != Eastern Cree */
+/*{HB_TAG('e','f','i',' '),	HB_TAG('E','F','I',' ')},*/	/* Efik */
+  {HB_TAG('e','k','k',' '),	HB_TAG('E','T','I',' ')},	/* Standard Estonian -> Estonian */
+  {HB_TAG('e','k','y',' '),	HB_TAG('K','R','N',' ')},	/* Eastern Kayah -> Karen */
+  {HB_TAG('e','m','k',' '),	HB_TAG('E','M','K',' ')},	/* Eastern Maninkakan */
+  {HB_TAG('e','m','k',' '),	HB_TAG('M','N','K',' ')},	/* Eastern Maninkakan -> Maninka */
+  {HB_TAG('e','m','y',' '),	HB_TAG('M','Y','N',' ')},	/* Epigraphic Mayan -> Mayan */
+  {HB_TAG('e','n','b',' '),	HB_TAG('K','A','L',' ')},	/* Markweeta -> Kalenjin */
+  {HB_TAG('e','n','f',' '),	HB_TAG('F','N','E',' ')},	/* Forest Enets */
+  {HB_TAG('e','n','h',' '),	HB_TAG('T','N','E',' ')},	/* Tundra Enets */
+  {HB_TAG('e','s','g',' '),	HB_TAG('G','O','N',' ')},	/* Aheri Gondi -> Gondi */
+  {HB_TAG('e','s','i',' '),	HB_TAG('I','P','K',' ')},	/* North Alaskan Inupiatun -> Inupiat */
+  {HB_TAG('e','s','k',' '),	HB_TAG('I','P','K',' ')},	/* Northwest Alaska Inupiatun -> Inupiat */
+/*{HB_TAG('e','s','u',' '),	HB_TAG('E','S','U',' ')},*/	/* Central Yupik */
+  {HB_TAG('e','t','o',' '),	HB_TAG('B','T','I',' ')},	/* Eton (Cameroon) -> Beti */
+  {HB_TAG('e','u','q',' '),	HB_TAG_NONE	       },	/* Basque [collection] != Basque */
+  {HB_TAG('e','v','e',' '),	HB_TAG('E','V','N',' ')},	/* Even */
+  {HB_TAG('e','v','n',' '),	HB_TAG('E','V','K',' ')},	/* Evenki */
+  {HB_TAG('e','w','o',' '),	HB_TAG('B','T','I',' ')},	/* Ewondo -> Beti */
+  {HB_TAG('e','y','o',' '),	HB_TAG('K','A','L',' ')},	/* Keiyo -> Kalenjin */
+  {HB_TAG('f','a','b',' '),	HB_TAG('C','P','P',' ')},	/* Fa d'Ambu -> Creoles */
+  {HB_TAG('f','a','n',' '),	HB_TAG('F','A','N','0')},	/* Fang (Equatorial Guinea) */
+  {HB_TAG('f','a','n',' '),	HB_TAG('B','T','I',' ')},	/* Fang (Equatorial Guinea) -> Beti */
+  {HB_TAG('f','a','r',' '),	HB_TAG_NONE	       },	/* Fataleka != Persian */
+  {HB_TAG('f','a','t',' '),	HB_TAG('F','A','T',' ')},	/* Fanti */
+  {HB_TAG('f','a','t',' '),	HB_TAG('A','K','A',' ')},	/* Fanti -> Akan */
+  {HB_TAG('f','b','l',' '),	HB_TAG('B','I','K',' ')},	/* West Albay Bikol -> Bikol */
+  {HB_TAG('f','f','m',' '),	HB_TAG('F','U','L',' ')},	/* Maasina Fulfulde -> Fulah */
+  {HB_TAG('f','i','l',' '),	HB_TAG('P','I','L',' ')},	/* Filipino */
+  {HB_TAG('f','l','m',' '),	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) (retired code) */
+  {HB_TAG('f','l','m',' '),	HB_TAG('Q','I','N',' ')},	/* Falam Chin (retired code) -> Chin */
+  {HB_TAG('f','m','p',' '),	HB_TAG('F','M','P',' ')},	/* Fe’fe’ */
+  {HB_TAG('f','m','p',' '),	HB_TAG('B','M','L',' ')},	/* Fe'fe' -> Bamileke */
+  {HB_TAG('f','n','g',' '),	HB_TAG('C','P','P',' ')},	/* Fanagalo -> Creoles */
+/*{HB_TAG('f','o','n',' '),	HB_TAG('F','O','N',' ')},*/	/* Fon */
+  {HB_TAG('f','o','s',' '),	HB_TAG_NONE	       },	/* Siraya != Faroese */
+  {HB_TAG('f','p','e',' '),	HB_TAG('C','P','P',' ')},	/* Fernando Po Creole English -> Creoles */
+/*{HB_TAG('f','r','c',' '),	HB_TAG('F','R','C',' ')},*/	/* Cajun French */
+/*{HB_TAG('f','r','p',' '),	HB_TAG('F','R','P',' ')},*/	/* Arpitan */
+  {HB_TAG('f','u','b',' '),	HB_TAG('F','U','L',' ')},	/* Adamawa Fulfulde -> Fulah */
+  {HB_TAG('f','u','c',' '),	HB_TAG('F','U','L',' ')},	/* Pulaar -> Fulah */
+  {HB_TAG('f','u','e',' '),	HB_TAG('F','U','L',' ')},	/* Borgu Fulfulde -> Fulah */
+  {HB_TAG('f','u','f',' '),	HB_TAG('F','T','A',' ')},	/* Pular -> Futa */
+  {HB_TAG('f','u','f',' '),	HB_TAG('F','U','L',' ')},	/* Pular -> Fulah */
+  {HB_TAG('f','u','h',' '),	HB_TAG('F','U','L',' ')},	/* Western Niger Fulfulde -> Fulah */
+  {HB_TAG('f','u','i',' '),	HB_TAG('F','U','L',' ')},	/* Bagirmi Fulfulde -> Fulah */
+  {HB_TAG('f','u','q',' '),	HB_TAG('F','U','L',' ')},	/* Central-Eastern Niger Fulfulde -> Fulah */
+  {HB_TAG('f','u','r',' '),	HB_TAG('F','R','L',' ')},	/* Friulian */
+  {HB_TAG('f','u','v',' '),	HB_TAG('F','U','V',' ')},	/* Nigerian Fulfulde */
+  {HB_TAG('f','u','v',' '),	HB_TAG('F','U','L',' ')},	/* Nigerian Fulfulde -> Fulah */
+  {HB_TAG('g','a','a',' '),	HB_TAG('G','A','D',' ')},	/* Ga */
+  {HB_TAG('g','a','c',' '),	HB_TAG('C','P','P',' ')},	/* Mixed Great Andamanese -> Creoles */
+  {HB_TAG('g','a','d',' '),	HB_TAG_NONE	       },	/* Gaddang != Ga */
+  {HB_TAG('g','a','e',' '),	HB_TAG_NONE	       },	/* Guarequena != Scottish Gaelic (Gaelic) */
+/*{HB_TAG('g','a','g',' '),	HB_TAG('G','A','G',' ')},*/	/* Gagauz */
+  {HB_TAG('g','a','l',' '),	HB_TAG_NONE	       },	/* Galolen != Galician */
+  {HB_TAG('g','a','n',' '),	HB_TAG('Z','H','S',' ')},	/* Gan Chinese -> Chinese, Simplified */
+  {HB_TAG('g','a','r',' '),	HB_TAG_NONE	       },	/* Galeya != Garshuni */
+  {HB_TAG('g','a','w',' '),	HB_TAG_NONE	       },	/* Nobonob != Garhwali */
+  {HB_TAG('g','a','x',' '),	HB_TAG('O','R','O',' ')},	/* Borana-Arsi-Guji Oromo -> Oromo */
+  {HB_TAG('g','a','z',' '),	HB_TAG('O','R','O',' ')},	/* West Central Oromo -> Oromo */
+  {HB_TAG('g','b','m',' '),	HB_TAG('G','A','W',' ')},	/* Garhwali */
+  {HB_TAG('g','c','e',' '),	HB_TAG('A','T','H',' ')},	/* Galice -> Athapaskan */
+  {HB_TAG('g','c','f',' '),	HB_TAG('C','P','P',' ')},	/* Guadeloupean Creole French -> Creoles */
+  {HB_TAG('g','c','l',' '),	HB_TAG('C','P','P',' ')},	/* Grenadian Creole English -> Creoles */
+  {HB_TAG('g','c','r',' '),	HB_TAG('C','P','P',' ')},	/* Guianese Creole French -> Creoles */
+  {HB_TAG('g','d','a',' '),	HB_TAG('R','A','J',' ')},	/* Gade Lohar -> Rajasthani */
+/*{HB_TAG('g','e','z',' '),	HB_TAG('G','E','Z',' ')},*/	/* Geez */
+  {HB_TAG('g','g','o',' '),	HB_TAG('G','O','N',' ')},	/* Southern Gondi (retired code) -> Gondi */
+  {HB_TAG('g','h','a',' '),	HB_TAG('B','B','R',' ')},	/* Ghadamès -> Berber */
+  {HB_TAG('g','h','k',' '),	HB_TAG('K','R','N',' ')},	/* Geko Karen -> Karen */
+  {HB_TAG('g','h','o',' '),	HB_TAG('B','B','R',' ')},	/* Ghomara -> Berber */
+  {HB_TAG('g','i','b',' '),	HB_TAG('C','P','P',' ')},	/* Gibanawa -> Creoles */
+/*{HB_TAG('g','i','h',' '),	HB_TAG('G','I','H',' ')},*/	/* Githabul */
+  {HB_TAG('g','i','l',' '),	HB_TAG('G','I','L','0')},	/* Kiribati (Gilbertese) */
+  {HB_TAG('g','j','u',' '),	HB_TAG('R','A','J',' ')},	/* Gujari -> Rajasthani */
+  {HB_TAG('g','k','p',' '),	HB_TAG('G','K','P',' ')},	/* Guinea Kpelle -> Kpelle (Guinea) */
+  {HB_TAG('g','k','p',' '),	HB_TAG('K','P','L',' ')},	/* Guinea Kpelle -> Kpelle */
+  {HB_TAG('g','l','d',' '),	HB_TAG('N','A','N',' ')},	/* Nanai */
+/*{HB_TAG('g','l','k',' '),	HB_TAG('G','L','K',' ')},*/	/* Gilaki */
+  {HB_TAG('g','m','z',' '),	HB_TAG_NONE	       },	/* Mgbolizhia != Gumuz */
+  {HB_TAG('g','n','b',' '),	HB_TAG('Q','I','N',' ')},	/* Gangte -> Chin */
+/*{HB_TAG('g','n','n',' '),	HB_TAG('G','N','N',' ')},*/	/* Gumatj */
+  {HB_TAG('g','n','o',' '),	HB_TAG('G','O','N',' ')},	/* Northern Gondi -> Gondi */
+  {HB_TAG('g','n','w',' '),	HB_TAG('G','U','A',' ')},	/* Western Bolivian Guaraní -> Guarani */
+/*{HB_TAG('g','o','g',' '),	HB_TAG('G','O','G',' ')},*/	/* Gogo */
+  {HB_TAG('g','o','m',' '),	HB_TAG('K','O','K',' ')},	/* Goan Konkani -> Konkani */
+/*{HB_TAG('g','o','n',' '),	HB_TAG('G','O','N',' ')},*/	/* Gondi [macrolanguage] */
+  {HB_TAG('g','o','q',' '),	HB_TAG('C','P','P',' ')},	/* Gorap -> Creoles */
+  {HB_TAG('g','o','x',' '),	HB_TAG('B','A','D','0')},	/* Gobu -> Banda */
+  {HB_TAG('g','p','e',' '),	HB_TAG('C','P','P',' ')},	/* Ghanaian Pidgin English -> Creoles */
+  {HB_TAG('g','r','o',' '),	HB_TAG_NONE	       },	/* Groma != Garo */
+  {HB_TAG('g','r','r',' '),	HB_TAG('B','B','R',' ')},	/* Taznatit -> Berber */
+  {HB_TAG('g','r','t',' '),	HB_TAG('G','R','O',' ')},	/* Garo */
+  {HB_TAG('g','r','u',' '),	HB_TAG('S','O','G',' ')},	/* Kistane -> Sodo Gurage */
+  {HB_TAG('g','s','w',' '),	HB_TAG('A','L','S',' ')},	/* Alsatian */
+  {HB_TAG('g','u','a',' '),	HB_TAG_NONE	       },	/* Shiki != Guarani */
+/*{HB_TAG('g','u','c',' '),	HB_TAG('G','U','C',' ')},*/	/* Wayuu */
+/*{HB_TAG('g','u','f',' '),	HB_TAG('G','U','F',' ')},*/	/* Gupapuyngu */
+  {HB_TAG('g','u','g',' '),	HB_TAG('G','U','A',' ')},	/* Paraguayan Guaraní -> Guarani */
+  {HB_TAG('g','u','i',' '),	HB_TAG('G','U','A',' ')},	/* Eastern Bolivian Guaraní -> Guarani */
+  {HB_TAG('g','u','k',' '),	HB_TAG('G','M','Z',' ')},	/* Gumuz */
+  {HB_TAG('g','u','l',' '),	HB_TAG('C','P','P',' ')},	/* Sea Island Creole English -> Creoles */
+  {HB_TAG('g','u','n',' '),	HB_TAG('G','U','A',' ')},	/* Mbyá Guaraní -> Guarani */
+/*{HB_TAG('g','u','z',' '),	HB_TAG('G','U','Z',' ')},*/	/* Gusii */
+  {HB_TAG('g','w','i',' '),	HB_TAG('A','T','H',' ')},	/* Gwichʼin -> Athapaskan */
+  {HB_TAG('g','y','n',' '),	HB_TAG('C','P','P',' ')},	/* Guyanese Creole English -> Creoles */
+  {HB_TAG('h','a','a',' '),	HB_TAG('A','T','H',' ')},	/* Han -> Athapaskan */
+  {HB_TAG('h','a','e',' '),	HB_TAG('O','R','O',' ')},	/* Eastern Oromo -> Oromo */
+  {HB_TAG('h','a','i',' '),	HB_TAG('H','A','I','0')},	/* Haida [macrolanguage] */
+  {HB_TAG('h','a','k',' '),	HB_TAG('Z','H','S',' ')},	/* Hakka Chinese -> Chinese, Simplified */
+  {HB_TAG('h','a','l',' '),	HB_TAG_NONE	       },	/* Halang != Halam (Falam Chin) */
+  {HB_TAG('h','a','r',' '),	HB_TAG('H','R','I',' ')},	/* Harari */
+/*{HB_TAG('h','a','w',' '),	HB_TAG('H','A','W',' ')},*/	/* Hawaiian */
+  {HB_TAG('h','a','x',' '),	HB_TAG('H','A','I','0')},	/* Southern Haida -> Haida */
+/*{HB_TAG('h','a','y',' '),	HB_TAG('H','A','Y',' ')},*/	/* Haya */
+/*{HB_TAG('h','a','z',' '),	HB_TAG('H','A','Z',' ')},*/	/* Hazaragi */
+  {HB_TAG('h','b','n',' '),	HB_TAG_NONE	       },	/* Heiban != Hammer-Banna */
+  {HB_TAG('h','c','a',' '),	HB_TAG('C','P','P',' ')},	/* Andaman Creole Hindi -> Creoles */
+  {HB_TAG('h','d','n',' '),	HB_TAG('H','A','I','0')},	/* Northern Haida -> Haida */
+  {HB_TAG('h','e','a',' '),	HB_TAG('H','M','N',' ')},	/* Northern Qiandong Miao -> Hmong */
+/*{HB_TAG('h','e','i',' '),	HB_TAG('H','E','I',' ')},*/	/* Heiltsuk */
+/*{HB_TAG('h','i','l',' '),	HB_TAG('H','I','L',' ')},*/	/* Hiligaynon */
+  {HB_TAG('h','j','i',' '),	HB_TAG('M','L','Y',' ')},	/* Haji -> Malay */
+  {HB_TAG('h','l','t',' '),	HB_TAG('Q','I','N',' ')},	/* Matu Chin -> Chin */
+  {HB_TAG('h','m','a',' '),	HB_TAG('H','M','N',' ')},	/* Southern Mashan Hmong -> Hmong */
+  {HB_TAG('h','m','c',' '),	HB_TAG('H','M','N',' ')},	/* Central Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','d',' '),	HB_TAG('H','M','D',' ')},	/* Large Flowery Miao -> A-Hmao */
+  {HB_TAG('h','m','d',' '),	HB_TAG('H','M','N',' ')},	/* Large Flowery Miao -> Hmong */
+  {HB_TAG('h','m','e',' '),	HB_TAG('H','M','N',' ')},	/* Eastern Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','g',' '),	HB_TAG('H','M','N',' ')},	/* Southwestern Guiyang Hmong -> Hmong */
+  {HB_TAG('h','m','h',' '),	HB_TAG('H','M','N',' ')},	/* Southwestern Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','i',' '),	HB_TAG('H','M','N',' ')},	/* Northern Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','j',' '),	HB_TAG('H','M','N',' ')},	/* Ge -> Hmong */
+  {HB_TAG('h','m','l',' '),	HB_TAG('H','M','N',' ')},	/* Luopohe Hmong -> Hmong */
+  {HB_TAG('h','m','m',' '),	HB_TAG('H','M','N',' ')},	/* Central Mashan Hmong -> Hmong */
+/*{HB_TAG('h','m','n',' '),	HB_TAG('H','M','N',' ')},*/	/* Hmong [macrolanguage] */
+  {HB_TAG('h','m','p',' '),	HB_TAG('H','M','N',' ')},	/* Northern Mashan Hmong -> Hmong */
+  {HB_TAG('h','m','q',' '),	HB_TAG('H','M','N',' ')},	/* Eastern Qiandong Miao -> Hmong */
+  {HB_TAG('h','m','r',' '),	HB_TAG('Q','I','N',' ')},	/* Hmar -> Chin */
+  {HB_TAG('h','m','s',' '),	HB_TAG('H','M','N',' ')},	/* Southern Qiandong Miao -> Hmong */
+  {HB_TAG('h','m','w',' '),	HB_TAG('H','M','N',' ')},	/* Western Mashan Hmong -> Hmong */
+  {HB_TAG('h','m','y',' '),	HB_TAG('H','M','N',' ')},	/* Southern Guiyang Hmong -> Hmong */
+  {HB_TAG('h','m','z',' '),	HB_TAG('H','M','Z',' ')},	/* Hmong Shua -> Hmong Shuat */
+  {HB_TAG('h','m','z',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Shua -> Hmong */
+/*{HB_TAG('h','n','d',' '),	HB_TAG('H','N','D',' ')},*/	/* Southern Hindko -> Hindko */
+  {HB_TAG('h','n','e',' '),	HB_TAG('C','H','H',' ')},	/* Chhattisgarhi -> Chattisgarhi */
+  {HB_TAG('h','n','j',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Njua -> Hmong */
+  {HB_TAG('h','n','o',' '),	HB_TAG('H','N','D',' ')},	/* Northern Hindko -> Hindko */
+  {HB_TAG('h','o','c',' '),	HB_TAG('H','O',' ',' ')},	/* Ho */
+  {HB_TAG('h','o','i',' '),	HB_TAG('A','T','H',' ')},	/* Holikachuk -> Athapaskan */
+  {HB_TAG('h','o','j',' '),	HB_TAG('H','A','R',' ')},	/* Hadothi -> Harauti */
+  {HB_TAG('h','o','j',' '),	HB_TAG('R','A','J',' ')},	/* Hadothi -> Rajasthani */
+  {HB_TAG('h','r','a',' '),	HB_TAG('Q','I','N',' ')},	/* Hrangkhol -> Chin */
+  {HB_TAG('h','r','m',' '),	HB_TAG('H','M','N',' ')},	/* Horned Miao -> Hmong */
+  {HB_TAG('h','s','b',' '),	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
+  {HB_TAG('h','s','n',' '),	HB_TAG('Z','H','S',' ')},	/* Xiang Chinese -> Chinese, Simplified */
+  {HB_TAG('h','u','j',' '),	HB_TAG('H','M','N',' ')},	/* Northern Guiyang Hmong -> Hmong */
+  {HB_TAG('h','u','p',' '),	HB_TAG('A','T','H',' ')},	/* Hupa -> Athapaskan */
+  {HB_TAG('h','u','s',' '),	HB_TAG('M','Y','N',' ')},	/* Huastec -> Mayan */
+  {HB_TAG('h','w','c',' '),	HB_TAG('C','P','P',' ')},	/* Hawai'i Creole English -> Creoles */
+  {HB_TAG('h','y','w',' '),	HB_TAG('H','Y','E',' ')},	/* Western Armenian -> Armenian */
+/*{HB_TAG('i','b','a',' '),	HB_TAG('I','B','A',' ')},*/	/* Iban */
+/*{HB_TAG('i','b','b',' '),	HB_TAG('I','B','B',' ')},*/	/* Ibibio */
+  {HB_TAG('i','b','y',' '),	HB_TAG('I','J','O',' ')},	/* Ibani -> Ijo */
+  {HB_TAG('i','c','r',' '),	HB_TAG('C','P','P',' ')},	/* Islander Creole English -> Creoles */
+  {HB_TAG('i','d','a',' '),	HB_TAG('L','U','H',' ')},	/* Idakho-Isukha-Tiriki -> Luyia */
+  {HB_TAG('i','d','b',' '),	HB_TAG('C','P','P',' ')},	/* Indo-Portuguese -> Creoles */
+  {HB_TAG('i','g','b',' '),	HB_TAG('E','B','I',' ')},	/* Ebira */
+  {HB_TAG('i','h','b',' '),	HB_TAG('C','P','P',' ')},	/* Iha Based Pidgin -> Creoles */
+  {HB_TAG('i','j','c',' '),	HB_TAG('I','J','O',' ')},	/* Izon -> Ijo */
+  {HB_TAG('i','j','e',' '),	HB_TAG('I','J','O',' ')},	/* Biseni -> Ijo */
+  {HB_TAG('i','j','n',' '),	HB_TAG('I','J','O',' ')},	/* Kalabari -> Ijo */
+/*{HB_TAG('i','j','o',' '),	HB_TAG('I','J','O',' ')},*/	/* Ijo [collection] */
+  {HB_TAG('i','j','s',' '),	HB_TAG('I','J','O',' ')},	/* Southeast Ijo -> Ijo */
+  {HB_TAG('i','k','e',' '),	HB_TAG('I','N','U',' ')},	/* Eastern Canadian Inuktitut -> Inuktitut */
+  {HB_TAG('i','k','e',' '),	HB_TAG('I','N','U','K')},	/* Eastern Canadian Inuktitut -> Nunavik Inuktitut */
+  {HB_TAG('i','k','t',' '),	HB_TAG('I','N','U',' ')},	/* Inuinnaqtun -> Inuktitut */
+/*{HB_TAG('i','l','o',' '),	HB_TAG('I','L','O',' ')},*/	/* Iloko -> Ilokano */
+  {HB_TAG('i','n','g',' '),	HB_TAG('A','T','H',' ')},	/* Degexit'an -> Athapaskan */
+  {HB_TAG('i','n','h',' '),	HB_TAG('I','N','G',' ')},	/* Ingush */
+  {HB_TAG('i','r','i',' '),	HB_TAG_NONE	       },	/* Rigwe != Irish */
+/*{HB_TAG('i','r','u',' '),	HB_TAG('I','R','U',' ')},*/	/* Irula */
+  {HB_TAG('i','s','m',' '),	HB_TAG_NONE	       },	/* Masimasi != Inari Sami */
+  {HB_TAG('i','t','z',' '),	HB_TAG('M','Y','N',' ')},	/* Itzá -> Mayan */
+  {HB_TAG('i','x','l',' '),	HB_TAG('M','Y','N',' ')},	/* Ixil -> Mayan */
+  {HB_TAG('j','a','c',' '),	HB_TAG('M','Y','N',' ')},	/* Popti' -> Mayan */
+  {HB_TAG('j','a','k',' '),	HB_TAG('M','L','Y',' ')},	/* Jakun -> Malay */
+  {HB_TAG('j','a','m',' '),	HB_TAG('J','A','M',' ')},	/* Jamaican Creole English -> Jamaican Creole */
+  {HB_TAG('j','a','m',' '),	HB_TAG('C','P','P',' ')},	/* Jamaican Creole English -> Creoles */
+  {HB_TAG('j','a','n',' '),	HB_TAG_NONE	       },	/* Jandai != Japanese */
+  {HB_TAG('j','a','x',' '),	HB_TAG('M','L','Y',' ')},	/* Jambi Malay -> Malay */
+  {HB_TAG('j','b','e',' '),	HB_TAG('B','B','R',' ')},	/* Judeo-Berber -> Berber */
+  {HB_TAG('j','b','n',' '),	HB_TAG('B','B','R',' ')},	/* Nafusi -> Berber */
+/*{HB_TAG('j','b','o',' '),	HB_TAG('J','B','O',' ')},*/	/* Lojban */
+/*{HB_TAG('j','c','t',' '),	HB_TAG('J','C','T',' ')},*/	/* Krymchak */
+  {HB_TAG('j','g','o',' '),	HB_TAG('B','M','L',' ')},	/* Ngomba -> Bamileke */
+  {HB_TAG('j','i','i',' '),	HB_TAG_NONE	       },	/* Jiiddu != Yiddish */
+  {HB_TAG('j','k','m',' '),	HB_TAG('K','R','N',' ')},	/* Mobwa Karen -> Karen */
+  {HB_TAG('j','k','p',' '),	HB_TAG('K','R','N',' ')},	/* Paku Karen -> Karen */
+  {HB_TAG('j','u','d',' '),	HB_TAG_NONE	       },	/* Worodougou != Ladino */
+  {HB_TAG('j','u','l',' '),	HB_TAG_NONE	       },	/* Jirel != Jula */
+  {HB_TAG('j','v','d',' '),	HB_TAG('C','P','P',' ')},	/* Javindo -> Creoles */
+  {HB_TAG('k','a','a',' '),	HB_TAG('K','R','K',' ')},	/* Karakalpak */
+  {HB_TAG('k','a','b',' '),	HB_TAG('K','A','B','0')},	/* Kabyle */
+  {HB_TAG('k','a','b',' '),	HB_TAG('B','B','R',' ')},	/* Kabyle -> Berber */
+  {HB_TAG('k','a','c',' '),	HB_TAG_NONE	       },	/* Kachin != Kachchi */
+  {HB_TAG('k','a','m',' '),	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
+  {HB_TAG('k','a','r',' '),	HB_TAG('K','R','N',' ')},	/* Karen [collection] */
+/*{HB_TAG('k','a','w',' '),	HB_TAG('K','A','W',' ')},*/	/* Kawi (Old Javanese) */
+  {HB_TAG('k','b','d',' '),	HB_TAG('K','A','B',' ')},	/* Kabardian */
+  {HB_TAG('k','b','y',' '),	HB_TAG('K','N','R',' ')},	/* Manga Kanuri -> Kanuri */
+  {HB_TAG('k','c','a',' '),	HB_TAG('K','H','K',' ')},	/* Khanty -> Khanty-Kazim */
+  {HB_TAG('k','c','a',' '),	HB_TAG('K','H','S',' ')},	/* Khanty -> Khanty-Shurishkar */
+  {HB_TAG('k','c','a',' '),	HB_TAG('K','H','V',' ')},	/* Khanty -> Khanty-Vakhi */
+  {HB_TAG('k','c','n',' '),	HB_TAG('C','P','P',' ')},	/* Nubi -> Creoles */
+/*{HB_TAG('k','d','e',' '),	HB_TAG('K','D','E',' ')},*/	/* Makonde */
+  {HB_TAG('k','d','r',' '),	HB_TAG('K','R','M',' ')},	/* Karaim */
+  {HB_TAG('k','d','t',' '),	HB_TAG('K','U','Y',' ')},	/* Kuy */
+  {HB_TAG('k','e','a',' '),	HB_TAG('K','E','A',' ')},	/* Kabuverdianu (Crioulo) */
+  {HB_TAG('k','e','a',' '),	HB_TAG('C','P','P',' ')},	/* Kabuverdianu -> Creoles */
+  {HB_TAG('k','e','b',' '),	HB_TAG_NONE	       },	/* Kélé != Kebena */
+  {HB_TAG('k','e','k',' '),	HB_TAG('K','E','K',' ')},	/* Kekchi */
+  {HB_TAG('k','e','k',' '),	HB_TAG('M','Y','N',' ')},	/* Kekchí -> Mayan */
+  {HB_TAG('k','e','x',' '),	HB_TAG('K','K','N',' ')},	/* Kukna -> Kokni */
+  {HB_TAG('k','f','a',' '),	HB_TAG('K','O','D',' ')},	/* Kodava -> Kodagu */
+  {HB_TAG('k','f','r',' '),	HB_TAG('K','A','C',' ')},	/* Kachhi -> Kachchi */
+  {HB_TAG('k','f','x',' '),	HB_TAG('K','U','L',' ')},	/* Kullu Pahari -> Kulvi */
+  {HB_TAG('k','f','y',' '),	HB_TAG('K','M','N',' ')},	/* Kumaoni */
+  {HB_TAG('k','g','e',' '),	HB_TAG_NONE	       },	/* Komering != Khutsuri Georgian */
+  {HB_TAG('k','h','a',' '),	HB_TAG('K','S','I',' ')},	/* Khasi */
+  {HB_TAG('k','h','b',' '),	HB_TAG('X','B','D',' ')},	/* Lü */
+  {HB_TAG('k','h','k',' '),	HB_TAG('M','N','G',' ')},	/* Halh Mongolian -> Mongolian */
+  {HB_TAG('k','h','n',' '),	HB_TAG_NONE	       },	/* Khandesi != Khamti Shan (Microsoft fonts) */
+  {HB_TAG('k','h','s',' '),	HB_TAG_NONE	       },	/* Kasua != Khanty-Shurishkar */
+  {HB_TAG('k','h','t',' '),	HB_TAG('K','H','T',' ')},	/* Khamti -> Khamti Shan */
+  {HB_TAG('k','h','t',' '),	HB_TAG('K','H','N',' ')},	/* Khamti -> Khamti Shan (Microsoft fonts) */
+  {HB_TAG('k','h','v',' '),	HB_TAG_NONE	       },	/* Khvarshi != Khanty-Vakhi */
+/*{HB_TAG('k','h','w',' '),	HB_TAG('K','H','W',' ')},*/	/* Khowar */
+  {HB_TAG('k','i','s',' '),	HB_TAG_NONE	       },	/* Kis != Kisii */
+  {HB_TAG('k','i','u',' '),	HB_TAG('K','I','U',' ')},	/* Kirmanjki */
+  {HB_TAG('k','i','u',' '),	HB_TAG('Z','Z','A',' ')},	/* Kirmanjki -> Zazaki */
+  {HB_TAG('k','j','b',' '),	HB_TAG('M','Y','N',' ')},	/* Q'anjob'al -> Mayan */
+/*{HB_TAG('k','j','d',' '),	HB_TAG('K','J','D',' ')},*/	/* Southern Kiwai */
+  {HB_TAG('k','j','h',' '),	HB_TAG('K','H','A',' ')},	/* Khakas -> Khakass */
+  {HB_TAG('k','j','p',' '),	HB_TAG('K','J','P',' ')},	/* Pwo Eastern Karen -> Eastern Pwo Karen */
+  {HB_TAG('k','j','p',' '),	HB_TAG('K','R','N',' ')},	/* Pwo Eastern Karen -> Karen */
+  {HB_TAG('k','j','t',' '),	HB_TAG('K','R','N',' ')},	/* Phrae Pwo Karen -> Karen */
+/*{HB_TAG('k','j','z',' '),	HB_TAG('K','J','Z',' ')},*/	/* Bumthangkha */
+  {HB_TAG('k','k','n',' '),	HB_TAG_NONE	       },	/* Kon Keu != Kokni */
+  {HB_TAG('k','k','z',' '),	HB_TAG('A','T','H',' ')},	/* Kaska -> Athapaskan */
+  {HB_TAG('k','l','m',' '),	HB_TAG_NONE	       },	/* Migum != Kalmyk */
+  {HB_TAG('k','l','n',' '),	HB_TAG('K','A','L',' ')},	/* Kalenjin [macrolanguage] */
+  {HB_TAG('k','m','b',' '),	HB_TAG('M','B','N',' ')},	/* Kimbundu -> Mbundu */
+  {HB_TAG('k','m','n',' '),	HB_TAG_NONE	       },	/* Awtuw != Kumaoni */
+  {HB_TAG('k','m','o',' '),	HB_TAG_NONE	       },	/* Kwoma != Komo */
+  {HB_TAG('k','m','r',' '),	HB_TAG('K','U','R',' ')},	/* Northern Kurdish -> Kurdish */
+  {HB_TAG('k','m','s',' '),	HB_TAG_NONE	       },	/* Kamasau != Komso */
+  {HB_TAG('k','m','v',' '),	HB_TAG('C','P','P',' ')},	/* Karipúna Creole French -> Creoles */
+  {HB_TAG('k','m','w',' '),	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
+/*{HB_TAG('k','m','z',' '),	HB_TAG('K','M','Z',' ')},*/	/* Khorasani Turkish -> Khorasani Turkic */
+  {HB_TAG('k','n','c',' '),	HB_TAG('K','N','R',' ')},	/* Central Kanuri -> Kanuri */
+  {HB_TAG('k','n','g',' '),	HB_TAG('K','O','N','0')},	/* Koongo -> Kongo */
+  {HB_TAG('k','n','j',' '),	HB_TAG('M','Y','N',' ')},	/* Western Kanjobal -> Mayan */
+  {HB_TAG('k','n','n',' '),	HB_TAG('K','O','K',' ')},	/* Konkani */
+  {HB_TAG('k','n','r',' '),	HB_TAG_NONE	       },	/* Kaningra != Kanuri */
+  {HB_TAG('k','o','d',' '),	HB_TAG_NONE	       },	/* Kodi != Kodagu */
+  {HB_TAG('k','o','h',' '),	HB_TAG_NONE	       },	/* Koyo != Korean Old Hangul */
+  {HB_TAG('k','o','i',' '),	HB_TAG('K','O','P',' ')},	/* Komi-Permyak */
+  {HB_TAG('k','o','i',' '),	HB_TAG('K','O','M',' ')},	/* Komi-Permyak -> Komi */
+/*{HB_TAG('k','o','k',' '),	HB_TAG('K','O','K',' ')},*/	/* Konkani [macrolanguage] */
+  {HB_TAG('k','o','p',' '),	HB_TAG_NONE	       },	/* Waube != Komi-Permyak */
+/*{HB_TAG('k','o','s',' '),	HB_TAG('K','O','S',' ')},*/	/* Kosraean */
+  {HB_TAG('k','o','y',' '),	HB_TAG('A','T','H',' ')},	/* Koyukon -> Athapaskan */
+  {HB_TAG('k','o','z',' '),	HB_TAG_NONE	       },	/* Korak != Komi-Zyrian */
+  {HB_TAG('k','p','e',' '),	HB_TAG('K','P','L',' ')},	/* Kpelle [macrolanguage] */
+  {HB_TAG('k','p','l',' '),	HB_TAG_NONE	       },	/* Kpala != Kpelle */
+  {HB_TAG('k','p','p',' '),	HB_TAG('K','R','N',' ')},	/* Paku Karen (retired code) -> Karen */
+  {HB_TAG('k','p','v',' '),	HB_TAG('K','O','Z',' ')},	/* Komi-Zyrian */
+  {HB_TAG('k','p','v',' '),	HB_TAG('K','O','M',' ')},	/* Komi-Zyrian -> Komi */
+  {HB_TAG('k','p','y',' '),	HB_TAG('K','Y','K',' ')},	/* Koryak */
+  {HB_TAG('k','q','s',' '),	HB_TAG('K','I','S',' ')},	/* Northern Kissi -> Kisii */
+  {HB_TAG('k','q','y',' '),	HB_TAG('K','R','T',' ')},	/* Koorete */
+  {HB_TAG('k','r','c',' '),	HB_TAG('K','A','R',' ')},	/* Karachay-Balkar -> Karachay */
+  {HB_TAG('k','r','c',' '),	HB_TAG('B','A','L',' ')},	/* Karachay-Balkar -> Balkar */
+  {HB_TAG('k','r','i',' '),	HB_TAG('K','R','I',' ')},	/* Krio */
+  {HB_TAG('k','r','i',' '),	HB_TAG('C','P','P',' ')},	/* Krio -> Creoles */
+  {HB_TAG('k','r','k',' '),	HB_TAG_NONE	       },	/* Kerek != Karakalpak */
+/*{HB_TAG('k','r','l',' '),	HB_TAG('K','R','L',' ')},*/	/* Karelian */
+  {HB_TAG('k','r','m',' '),	HB_TAG_NONE	       },	/* Krim (retired code) != Karaim */
+  {HB_TAG('k','r','n',' '),	HB_TAG_NONE	       },	/* Sapo != Karen */
+  {HB_TAG('k','r','t',' '),	HB_TAG('K','N','R',' ')},	/* Tumari Kanuri -> Kanuri */
+  {HB_TAG('k','r','u',' '),	HB_TAG('K','U','U',' ')},	/* Kurukh */
+  {HB_TAG('k','s','h',' '),	HB_TAG('K','S','H','0')},	/* Kölsch -> Ripuarian */
+  {HB_TAG('k','s','i',' '),	HB_TAG_NONE	       },	/* Krisa != Khasi */
+  {HB_TAG('k','s','m',' '),	HB_TAG_NONE	       },	/* Kumba != Kildin Sami */
+  {HB_TAG('k','s','s',' '),	HB_TAG('K','I','S',' ')},	/* Southern Kisi -> Kisii */
+  {HB_TAG('k','s','w',' '),	HB_TAG('K','S','W',' ')},	/* S’gaw Karen */
+  {HB_TAG('k','s','w',' '),	HB_TAG('K','R','N',' ')},	/* S'gaw Karen -> Karen */
+  {HB_TAG('k','t','b',' '),	HB_TAG('K','E','B',' ')},	/* Kambaata -> Kebena */
+  {HB_TAG('k','t','u',' '),	HB_TAG('K','O','N',' ')},	/* Kituba (Democratic Republic of Congo) -> Kikongo */
+  {HB_TAG('k','t','w',' '),	HB_TAG('A','T','H',' ')},	/* Kato -> Athapaskan */
+  {HB_TAG('k','u','i',' '),	HB_TAG_NONE	       },	/* Kuikúro-Kalapálo != Kui */
+  {HB_TAG('k','u','l',' '),	HB_TAG_NONE	       },	/* Kulere != Kulvi */
+/*{HB_TAG('k','u','m',' '),	HB_TAG('K','U','M',' ')},*/	/* Kumyk */
+  {HB_TAG('k','u','u',' '),	HB_TAG('A','T','H',' ')},	/* Upper Kuskokwim -> Athapaskan */
+  {HB_TAG('k','u','w',' '),	HB_TAG('B','A','D','0')},	/* Kpagua -> Banda */
+  {HB_TAG('k','u','y',' '),	HB_TAG_NONE	       },	/* Kuuku-Ya'u != Kuy */
+  {HB_TAG('k','v','b',' '),	HB_TAG('M','L','Y',' ')},	/* Kubu -> Malay */
+  {HB_TAG('k','v','l',' '),	HB_TAG('K','R','N',' ')},	/* Kayaw -> Karen */
+  {HB_TAG('k','v','q',' '),	HB_TAG('K','R','N',' ')},	/* Geba Karen -> Karen */
+  {HB_TAG('k','v','r',' '),	HB_TAG('M','L','Y',' ')},	/* Kerinci -> Malay */
+  {HB_TAG('k','v','t',' '),	HB_TAG('K','R','N',' ')},	/* Lahta Karen -> Karen */
+  {HB_TAG('k','v','u',' '),	HB_TAG('K','R','N',' ')},	/* Yinbaw Karen -> Karen */
+  {HB_TAG('k','v','y',' '),	HB_TAG('K','R','N',' ')},	/* Yintale Karen -> Karen */
+/*{HB_TAG('k','w','k',' '),	HB_TAG('K','W','K',' ')},*/	/* Kwakiutl -> Kwakʼwala */
+  {HB_TAG('k','w','w',' '),	HB_TAG('C','P','P',' ')},	/* Kwinti -> Creoles */
+  {HB_TAG('k','w','y',' '),	HB_TAG('K','O','N','0')},	/* San Salvador Kongo -> Kongo */
+  {HB_TAG('k','x','c',' '),	HB_TAG('K','M','S',' ')},	/* Konso -> Komso */
+  {HB_TAG('k','x','d',' '),	HB_TAG('M','L','Y',' ')},	/* Brunei -> Malay */
+  {HB_TAG('k','x','f',' '),	HB_TAG('K','R','N',' ')},	/* Manumanaw Karen -> Karen */
+  {HB_TAG('k','x','k',' '),	HB_TAG('K','R','N',' ')},	/* Zayein Karen -> Karen */
+  {HB_TAG('k','x','l',' '),	HB_TAG('K','U','U',' ')},	/* Nepali Kurux (retired code) -> Kurukh */
+  {HB_TAG('k','x','u',' '),	HB_TAG('K','U','I',' ')},	/* Kui (India) (retired code) */
+  {HB_TAG('k','y','k',' '),	HB_TAG_NONE	       },	/* Kamayo != Koryak */
+  {HB_TAG('k','y','u',' '),	HB_TAG('K','Y','U',' ')},	/* Western Kayah */
+  {HB_TAG('k','y','u',' '),	HB_TAG('K','R','N',' ')},	/* Western Kayah -> Karen */
+  {HB_TAG('l','a','c',' '),	HB_TAG('M','Y','N',' ')},	/* Lacandon -> Mayan */
+  {HB_TAG('l','a','d',' '),	HB_TAG('J','U','D',' ')},	/* Ladino */
+  {HB_TAG('l','a','h',' '),	HB_TAG_NONE	       },	/* Lahnda [macrolanguage] != Lahuli */
+  {HB_TAG('l','a','k',' '),	HB_TAG_NONE	       },	/* Laka (Nigeria) (retired code) != Lak */
+  {HB_TAG('l','a','m',' '),	HB_TAG_NONE	       },	/* Lamba != Lambani */
+  {HB_TAG('l','a','z',' '),	HB_TAG_NONE	       },	/* Aribwatsa != Laz */
+  {HB_TAG('l','b','e',' '),	HB_TAG('L','A','K',' ')},	/* Lak */
+  {HB_TAG('l','b','j',' '),	HB_TAG('L','D','K',' ')},	/* Ladakhi */
+  {HB_TAG('l','b','l',' '),	HB_TAG('B','I','K',' ')},	/* Libon Bikol -> Bikol */
+  {HB_TAG('l','c','e',' '),	HB_TAG('M','L','Y',' ')},	/* Loncong -> Malay */
+  {HB_TAG('l','c','f',' '),	HB_TAG('M','L','Y',' ')},	/* Lubu -> Malay */
+  {HB_TAG('l','d','i',' '),	HB_TAG('K','O','N','0')},	/* Laari -> Kongo */
+  {HB_TAG('l','d','k',' '),	HB_TAG_NONE	       },	/* Leelau != Ladakhi */
+/*{HB_TAG('l','e','f',' '),	HB_TAG('L','E','F',' ')},*/	/* Lelemi */
+/*{HB_TAG('l','e','z',' '),	HB_TAG('L','E','Z',' ')},*/	/* Lezghian -> Lezgi */
+  {HB_TAG('l','i','f',' '),	HB_TAG('L','M','B',' ')},	/* Limbu */
+/*{HB_TAG('l','i','j',' '),	HB_TAG('L','I','J',' ')},*/	/* Ligurian */
+  {HB_TAG('l','i','r',' '),	HB_TAG('C','P','P',' ')},	/* Liberian English -> Creoles */
+/*{HB_TAG('l','i','s',' '),	HB_TAG('L','I','S',' ')},*/	/* Lisu */
+  {HB_TAG('l','i','w',' '),	HB_TAG('M','L','Y',' ')},	/* Col -> Malay */
+  {HB_TAG('l','i','y',' '),	HB_TAG('B','A','D','0')},	/* Banda-Bambari -> Banda */
+/*{HB_TAG('l','j','p',' '),	HB_TAG('L','J','P',' ')},*/	/* Lampung Api -> Lampung */
+  {HB_TAG('l','k','b',' '),	HB_TAG('L','U','H',' ')},	/* Kabras -> Luyia */
+/*{HB_TAG('l','k','i',' '),	HB_TAG('L','K','I',' ')},*/	/* Laki */
+  {HB_TAG('l','k','o',' '),	HB_TAG('L','U','H',' ')},	/* Khayo -> Luyia */
+  {HB_TAG('l','k','s',' '),	HB_TAG('L','U','H',' ')},	/* Kisa -> Luyia */
+  {HB_TAG('l','l','d',' '),	HB_TAG('L','A','D',' ')},	/* Ladin */
+  {HB_TAG('l','m','a',' '),	HB_TAG_NONE	       },	/* East Limba != Low Mari */
+  {HB_TAG('l','m','b',' '),	HB_TAG_NONE	       },	/* Merei != Limbu */
+  {HB_TAG('l','m','n',' '),	HB_TAG('L','A','M',' ')},	/* Lambadi -> Lambani */
+/*{HB_TAG('l','m','o',' '),	HB_TAG('L','M','O',' ')},*/	/* Lombard */
+  {HB_TAG('l','m','w',' '),	HB_TAG_NONE	       },	/* Lake Miwok != Lomwe */
+  {HB_TAG('l','n','a',' '),	HB_TAG('B','A','D','0')},	/* Langbashe -> Banda */
+  {HB_TAG('l','n','l',' '),	HB_TAG('B','A','D','0')},	/* South Central Banda -> Banda */
+/*{HB_TAG('l','o','m',' '),	HB_TAG('L','O','M',' ')},*/	/* Loma (Liberia) */
+  {HB_TAG('l','o','u',' '),	HB_TAG('C','P','P',' ')},	/* Louisiana Creole -> Creoles */
+/*{HB_TAG('l','p','o',' '),	HB_TAG('L','P','O',' ')},*/	/* Lipo */
+/*{HB_TAG('l','r','c',' '),	HB_TAG('L','R','C',' ')},*/	/* Northern Luri -> Luri */
+  {HB_TAG('l','r','i',' '),	HB_TAG('L','U','H',' ')},	/* Marachi -> Luyia */
+  {HB_TAG('l','r','m',' '),	HB_TAG('L','U','H',' ')},	/* Marama -> Luyia */
+  {HB_TAG('l','r','t',' '),	HB_TAG('C','P','P',' ')},	/* Larantuka Malay -> Creoles */
+  {HB_TAG('l','s','b',' '),	HB_TAG_NONE	       },	/* Burundian Sign Language != Lower Sorbian */
+  {HB_TAG('l','s','m',' '),	HB_TAG('L','U','H',' ')},	/* Saamia -> Luyia */
+  {HB_TAG('l','t','g',' '),	HB_TAG('L','V','I',' ')},	/* Latgalian -> Latvian */
+  {HB_TAG('l','t','h',' '),	HB_TAG_NONE	       },	/* Thur != Lithuanian */
+  {HB_TAG('l','t','o',' '),	HB_TAG('L','U','H',' ')},	/* Tsotso -> Luyia */
+  {HB_TAG('l','t','s',' '),	HB_TAG('L','U','H',' ')},	/* Tachoni -> Luyia */
+/*{HB_TAG('l','u','a',' '),	HB_TAG('L','U','A',' ')},*/	/* Luba-Lulua */
+/*{HB_TAG('l','u','o',' '),	HB_TAG('L','U','O',' ')},*/	/* Luo (Kenya and Tanzania) */
+  {HB_TAG('l','u','s',' '),	HB_TAG('M','I','Z',' ')},	/* Lushai -> Mizo */
+  {HB_TAG('l','u','s',' '),	HB_TAG('Q','I','N',' ')},	/* Lushai -> Chin */
+  {HB_TAG('l','u','y',' '),	HB_TAG('L','U','H',' ')},	/* Luyia [macrolanguage] */
+  {HB_TAG('l','u','z',' '),	HB_TAG('L','R','C',' ')},	/* Southern Luri -> Luri */
+  {HB_TAG('l','v','i',' '),	HB_TAG_NONE	       },	/* Lavi != Latvian */
+  {HB_TAG('l','v','s',' '),	HB_TAG('L','V','I',' ')},	/* Standard Latvian -> Latvian */
+  {HB_TAG('l','w','g',' '),	HB_TAG('L','U','H',' ')},	/* Wanga -> Luyia */
+  {HB_TAG('l','z','h',' '),	HB_TAG('Z','H','T',' ')},	/* Literary Chinese -> Chinese, Traditional */
+  {HB_TAG('l','z','z',' '),	HB_TAG('L','A','Z',' ')},	/* Laz */
+/*{HB_TAG('m','a','d',' '),	HB_TAG('M','A','D',' ')},*/	/* Madurese -> Madura */
+/*{HB_TAG('m','a','g',' '),	HB_TAG('M','A','G',' ')},*/	/* Magahi */
+  {HB_TAG('m','a','i',' '),	HB_TAG('M','T','H',' ')},	/* Maithili */
+  {HB_TAG('m','a','j',' '),	HB_TAG_NONE	       },	/* Jalapa De Díaz Mazatec != Majang */
+  {HB_TAG('m','a','k',' '),	HB_TAG('M','K','R',' ')},	/* Makasar */
+  {HB_TAG('m','a','m',' '),	HB_TAG('M','A','M',' ')},	/* Mam */
+  {HB_TAG('m','a','m',' '),	HB_TAG('M','Y','N',' ')},	/* Mam -> Mayan */
+  {HB_TAG('m','a','n',' '),	HB_TAG('M','N','K',' ')},	/* Mandingo [macrolanguage] -> Maninka */
+  {HB_TAG('m','a','p',' '),	HB_TAG_NONE	       },	/* Austronesian [collection] != Mapudungun */
+  {HB_TAG('m','a','w',' '),	HB_TAG_NONE	       },	/* Mampruli != Marwari */
+  {HB_TAG('m','a','x',' '),	HB_TAG('M','L','Y',' ')},	/* North Moluccan Malay -> Malay */
+  {HB_TAG('m','a','x',' '),	HB_TAG('C','P','P',' ')},	/* North Moluccan Malay -> Creoles */
+  {HB_TAG('m','b','f',' '),	HB_TAG('C','P','P',' ')},	/* Baba Malay -> Creoles */
+  {HB_TAG('m','b','n',' '),	HB_TAG_NONE	       },	/* Macaguán != Mbundu */
+/*{HB_TAG('m','b','o',' '),	HB_TAG('M','B','O',' ')},*/	/* Mbo (Cameroon) */
+  {HB_TAG('m','c','h',' '),	HB_TAG_NONE	       },	/* Maquiritari != Manchu */
+  {HB_TAG('m','c','m',' '),	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Portuguese -> Creoles */
+  {HB_TAG('m','c','r',' '),	HB_TAG_NONE	       },	/* Menya != Moose Cree */
+  {HB_TAG('m','c','t',' '),	HB_TAG('B','T','I',' ')},	/* Mengisa -> Beti */
+  {HB_TAG('m','d','e',' '),	HB_TAG_NONE	       },	/* Maba (Chad) != Mende */
+  {HB_TAG('m','d','f',' '),	HB_TAG('M','O','K',' ')},	/* Moksha */
+/*{HB_TAG('m','d','r',' '),	HB_TAG('M','D','R',' ')},*/	/* Mandar */
+  {HB_TAG('m','d','y',' '),	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
+  {HB_TAG('m','e','n',' '),	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
+  {HB_TAG('m','e','o',' '),	HB_TAG('M','L','Y',' ')},	/* Kedah Malay -> Malay */
+/*{HB_TAG('m','e','r',' '),	HB_TAG('M','E','R',' ')},*/	/* Meru */
+  {HB_TAG('m','f','a',' '),	HB_TAG('M','F','A',' ')},	/* Pattani Malay */
+  {HB_TAG('m','f','a',' '),	HB_TAG('M','L','Y',' ')},	/* Pattani Malay -> Malay */
+  {HB_TAG('m','f','b',' '),	HB_TAG('M','L','Y',' ')},	/* Bangka -> Malay */
+  {HB_TAG('m','f','e',' '),	HB_TAG('M','F','E',' ')},	/* Morisyen */
+  {HB_TAG('m','f','e',' '),	HB_TAG('C','P','P',' ')},	/* Morisyen -> Creoles */
+  {HB_TAG('m','f','p',' '),	HB_TAG('C','P','P',' ')},	/* Makassar Malay -> Creoles */
+  {HB_TAG('m','h','c',' '),	HB_TAG('M','Y','N',' ')},	/* Mocho -> Mayan */
+  {HB_TAG('m','h','r',' '),	HB_TAG('L','M','A',' ')},	/* Eastern Mari -> Low Mari */
+  {HB_TAG('m','h','v',' '),	HB_TAG('A','R','K',' ')},	/* Arakanese (retired code) -> Rakhine */
+  {HB_TAG('m','i','n',' '),	HB_TAG('M','I','N',' ')},	/* Minangkabau */
+  {HB_TAG('m','i','n',' '),	HB_TAG('M','L','Y',' ')},	/* Minangkabau -> Malay */
+  {HB_TAG('m','i','z',' '),	HB_TAG_NONE	       },	/* Coatzospan Mixtec != Mizo */
+  {HB_TAG('m','k','n',' '),	HB_TAG('C','P','P',' ')},	/* Kupang Malay -> Creoles */
+  {HB_TAG('m','k','r',' '),	HB_TAG_NONE	       },	/* Malas != Makasar */
+  {HB_TAG('m','k','u',' '),	HB_TAG('M','N','K',' ')},	/* Konyanka Maninka -> Maninka */
+/*{HB_TAG('m','k','w',' '),	HB_TAG('M','K','W',' ')},*/	/* Kituba (Congo) */
+  {HB_TAG('m','l','e',' '),	HB_TAG_NONE	       },	/* Manambu != Male */
+  {HB_TAG('m','l','n',' '),	HB_TAG_NONE	       },	/* Malango != Malinke */
+  {HB_TAG('m','l','q',' '),	HB_TAG('M','L','N',' ')},	/* Western Maninkakan -> Malinke */
+  {HB_TAG('m','l','q',' '),	HB_TAG('M','N','K',' ')},	/* Western Maninkakan -> Maninka */
+  {HB_TAG('m','l','r',' '),	HB_TAG_NONE	       },	/* Vame != Malayalam Reformed */
+  {HB_TAG('m','m','r',' '),	HB_TAG('H','M','N',' ')},	/* Western Xiangxi Miao -> Hmong */
+  {HB_TAG('m','n','c',' '),	HB_TAG('M','C','H',' ')},	/* Manchu */
+  {HB_TAG('m','n','d',' '),	HB_TAG_NONE	       },	/* Mondé != Mandinka */
+  {HB_TAG('m','n','g',' '),	HB_TAG_NONE	       },	/* Eastern Mnong != Mongolian */
+  {HB_TAG('m','n','h',' '),	HB_TAG('B','A','D','0')},	/* Mono (Democratic Republic of Congo) -> Banda */
+/*{HB_TAG('m','n','i',' '),	HB_TAG('M','N','I',' ')},*/	/* Manipuri */
+  {HB_TAG('m','n','k',' '),	HB_TAG('M','N','D',' ')},	/* Mandinka */
+  {HB_TAG('m','n','k',' '),	HB_TAG('M','N','K',' ')},	/* Mandinka -> Maninka */
+  {HB_TAG('m','n','p',' '),	HB_TAG('Z','H','S',' ')},	/* Min Bei Chinese -> Chinese, Simplified */
+  {HB_TAG('m','n','s',' '),	HB_TAG('M','A','N',' ')},	/* Mansi */
+  {HB_TAG('m','n','w',' '),	HB_TAG('M','O','N',' ')},	/* Mon */
+  {HB_TAG('m','n','w',' '),	HB_TAG('M','O','N','T')},	/* Mon -> Thailand Mon */
+  {HB_TAG('m','n','x',' '),	HB_TAG_NONE	       },	/* Manikion != Manx */
+  {HB_TAG('m','o','d',' '),	HB_TAG('C','P','P',' ')},	/* Mobilian -> Creoles */
+/*{HB_TAG('m','o','h',' '),	HB_TAG('M','O','H',' ')},*/	/* Mohawk */
+  {HB_TAG('m','o','k',' '),	HB_TAG_NONE	       },	/* Morori != Moksha */
+  {HB_TAG('m','o','p',' '),	HB_TAG('M','Y','N',' ')},	/* Mopán Maya -> Mayan */
+  {HB_TAG('m','o','r',' '),	HB_TAG_NONE	       },	/* Moro != Moroccan */
+/*{HB_TAG('m','o','s',' '),	HB_TAG('M','O','S',' ')},*/	/* Mossi */
+  {HB_TAG('m','p','e',' '),	HB_TAG('M','A','J',' ')},	/* Majang */
+  {HB_TAG('m','q','g',' '),	HB_TAG('M','L','Y',' ')},	/* Kota Bangun Kutai Malay -> Malay */
+  {HB_TAG('m','r','h',' '),	HB_TAG('Q','I','N',' ')},	/* Mara Chin -> Chin */
+  {HB_TAG('m','r','j',' '),	HB_TAG('H','M','A',' ')},	/* Western Mari -> High Mari */
+  {HB_TAG('m','s','c',' '),	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka -> Maninka */
+  {HB_TAG('m','s','h',' '),	HB_TAG('M','L','G',' ')},	/* Masikoro Malagasy -> Malagasy */
+  {HB_TAG('m','s','i',' '),	HB_TAG('M','L','Y',' ')},	/* Sabah Malay -> Malay */
+  {HB_TAG('m','s','i',' '),	HB_TAG('C','P','P',' ')},	/* Sabah Malay -> Creoles */
+  {HB_TAG('m','t','h',' '),	HB_TAG_NONE	       },	/* Munggui != Maithili */
+  {HB_TAG('m','t','r',' '),	HB_TAG('M','A','W',' ')},	/* Mewari -> Marwari */
+  {HB_TAG('m','t','s',' '),	HB_TAG_NONE	       },	/* Yora != Maltese */
+  {HB_TAG('m','u','d',' '),	HB_TAG('C','P','P',' ')},	/* Mednyj Aleut -> Creoles */
+  {HB_TAG('m','u','i',' '),	HB_TAG('M','L','Y',' ')},	/* Musi -> Malay */
+  {HB_TAG('m','u','n',' '),	HB_TAG_NONE	       },	/* Munda [collection] != Mundari */
+  {HB_TAG('m','u','p',' '),	HB_TAG('R','A','J',' ')},	/* Malvi -> Rajasthani */
+  {HB_TAG('m','u','q',' '),	HB_TAG('H','M','N',' ')},	/* Eastern Xiangxi Miao -> Hmong */
+/*{HB_TAG('m','u','s',' '),	HB_TAG('M','U','S',' ')},*/	/* Creek -> Muscogee */
+  {HB_TAG('m','v','b',' '),	HB_TAG('A','T','H',' ')},	/* Mattole -> Athapaskan */
+  {HB_TAG('m','v','e',' '),	HB_TAG('M','A','W',' ')},	/* Marwari (Pakistan) */
+  {HB_TAG('m','v','f',' '),	HB_TAG('M','N','G',' ')},	/* Peripheral Mongolian -> Mongolian */
+  {HB_TAG('m','w','k',' '),	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan -> Maninka */
+/*{HB_TAG('m','w','l',' '),	HB_TAG('M','W','L',' ')},*/	/* Mirandese */
+  {HB_TAG('m','w','q',' '),	HB_TAG('Q','I','N',' ')},	/* Mün Chin -> Chin */
+  {HB_TAG('m','w','r',' '),	HB_TAG('M','A','W',' ')},	/* Marwari [macrolanguage] */
+  {HB_TAG('m','w','w',' '),	HB_TAG('M','W','W',' ')},	/* Hmong Daw */
+  {HB_TAG('m','w','w',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Daw -> Hmong */
+  {HB_TAG('m','y','m',' '),	HB_TAG('M','E','N',' ')},	/* Me’en */
+/*{HB_TAG('m','y','n',' '),	HB_TAG('M','Y','N',' ')},*/	/* Mayan [collection] */
+  {HB_TAG('m','y','q',' '),	HB_TAG('M','N','K',' ')},	/* Forest Maninka (retired code) -> Maninka */
+  {HB_TAG('m','y','v',' '),	HB_TAG('E','R','Z',' ')},	/* Erzya */
+  {HB_TAG('m','z','b',' '),	HB_TAG('B','B','R',' ')},	/* Tumzabt -> Berber */
+/*{HB_TAG('m','z','n',' '),	HB_TAG('M','Z','N',' ')},*/	/* Mazanderani */
+  {HB_TAG('m','z','s',' '),	HB_TAG('C','P','P',' ')},	/* Macanese -> Creoles */
+  {HB_TAG('n','a','g',' '),	HB_TAG('N','A','G',' ')},	/* Naga Pidgin -> Naga-Assamese */
+  {HB_TAG('n','a','g',' '),	HB_TAG('C','P','P',' ')},	/* Naga Pidgin -> Creoles */
+/*{HB_TAG('n','a','h',' '),	HB_TAG('N','A','H',' ')},*/	/* Nahuatl [collection] */
+  {HB_TAG('n','a','n',' '),	HB_TAG('Z','H','S',' ')},	/* Min Nan Chinese -> Chinese, Simplified */
+/*{HB_TAG('n','a','p',' '),	HB_TAG('N','A','P',' ')},*/	/* Neapolitan */
+  {HB_TAG('n','a','s',' '),	HB_TAG_NONE	       },	/* Naasioi != Naskapi */
+  {HB_TAG('n','a','z',' '),	HB_TAG('N','A','H',' ')},	/* Coatepec Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','h',' '),	HB_TAG('N','A','H',' ')},	/* Central Huasteca Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','i',' '),	HB_TAG('N','A','H',' ')},	/* Classical Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','j',' '),	HB_TAG('N','A','H',' ')},	/* Northern Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','l',' '),	HB_TAG('N','A','H',' ')},	/* Michoacán Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','r',' '),	HB_TAG_NONE	       },	/* Ncane != N-Cree */
+  {HB_TAG('n','c','x',' '),	HB_TAG('N','A','H',' ')},	/* Central Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('n','d','b',' '),	HB_TAG_NONE	       },	/* Kenswei Nsei != Ndebele */
+/*{HB_TAG('n','d','c',' '),	HB_TAG('N','D','C',' ')},*/	/* Ndau */
+  {HB_TAG('n','d','g',' '),	HB_TAG_NONE	       },	/* Ndengereko != Ndonga */
+/*{HB_TAG('n','d','s',' '),	HB_TAG('N','D','S',' ')},*/	/* Low Saxon */
+  {HB_TAG('n','e','f',' '),	HB_TAG('C','P','P',' ')},	/* Nefamese -> Creoles */
+/*{HB_TAG('n','e','w',' '),	HB_TAG('N','E','W',' ')},*/	/* Newari */
+/*{HB_TAG('n','g','a',' '),	HB_TAG('N','G','A',' ')},*/	/* Ngbaka */
+  {HB_TAG('n','g','l',' '),	HB_TAG('L','M','W',' ')},	/* Lomwe */
+  {HB_TAG('n','g','m',' '),	HB_TAG('C','P','P',' ')},	/* Ngatik Men's Creole -> Creoles */
+  {HB_TAG('n','g','o',' '),	HB_TAG('S','X','T',' ')},	/* Ngoni (retired code) -> Sutu */
+  {HB_TAG('n','g','r',' '),	HB_TAG_NONE	       },	/* Engdewu != Nagari */
+  {HB_TAG('n','g','u',' '),	HB_TAG('N','A','H',' ')},	/* Guerrero Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','c',' '),	HB_TAG('N','A','H',' ')},	/* Tabasco Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','d',' '),	HB_TAG('G','U','A',' ')},	/* Chiripá -> Guarani */
+  {HB_TAG('n','h','e',' '),	HB_TAG('N','A','H',' ')},	/* Eastern Huasteca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','g',' '),	HB_TAG('N','A','H',' ')},	/* Tetelcingo Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','i',' '),	HB_TAG('N','A','H',' ')},	/* Zacatlán-Ahuacatlán-Tepetzintla Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','k',' '),	HB_TAG('N','A','H',' ')},	/* Isthmus-Cosoleacaque Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','m',' '),	HB_TAG('N','A','H',' ')},	/* Morelos Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','n',' '),	HB_TAG('N','A','H',' ')},	/* Central Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','p',' '),	HB_TAG('N','A','H',' ')},	/* Isthmus-Pajapan Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','q',' '),	HB_TAG('N','A','H',' ')},	/* Huaxcaleca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','t',' '),	HB_TAG('N','A','H',' ')},	/* Ometepec Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','v',' '),	HB_TAG('N','A','H',' ')},	/* Temascaltepec Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','w',' '),	HB_TAG('N','A','H',' ')},	/* Western Huasteca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','x',' '),	HB_TAG('N','A','H',' ')},	/* Isthmus-Mecayapan Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','y',' '),	HB_TAG('N','A','H',' ')},	/* Northern Oaxaca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','z',' '),	HB_TAG('N','A','H',' ')},	/* Santa María La Alta Nahuatl -> Nahuatl */
+  {HB_TAG('n','i','q',' '),	HB_TAG('K','A','L',' ')},	/* Nandi -> Kalenjin */
+  {HB_TAG('n','i','s',' '),	HB_TAG_NONE	       },	/* Nimi != Nisi */
+/*{HB_TAG('n','i','u',' '),	HB_TAG('N','I','U',' ')},*/	/* Niuean */
+  {HB_TAG('n','i','v',' '),	HB_TAG('G','I','L',' ')},	/* Gilyak */
+  {HB_TAG('n','j','t',' '),	HB_TAG('C','P','P',' ')},	/* Ndyuka-Trio Pidgin -> Creoles */
+  {HB_TAG('n','j','z',' '),	HB_TAG('N','I','S',' ')},	/* Nyishi -> Nisi */
+  {HB_TAG('n','k','o',' '),	HB_TAG_NONE	       },	/* Nkonya != N’Ko */
+  {HB_TAG('n','k','x',' '),	HB_TAG('I','J','O',' ')},	/* Nkoroo -> Ijo */
+  {HB_TAG('n','l','a',' '),	HB_TAG('B','M','L',' ')},	/* Ngombale -> Bamileke */
+  {HB_TAG('n','l','e',' '),	HB_TAG('L','U','H',' ')},	/* East Nyala -> Luyia */
+  {HB_TAG('n','l','n',' '),	HB_TAG('N','A','H',' ')},	/* Durango Nahuatl (retired code) -> Nahuatl */
+  {HB_TAG('n','l','v',' '),	HB_TAG('N','A','H',' ')},	/* Orizaba Nahuatl -> Nahuatl */
+  {HB_TAG('n','n','h',' '),	HB_TAG('B','M','L',' ')},	/* Ngiemboon -> Bamileke */
+  {HB_TAG('n','n','z',' '),	HB_TAG('B','M','L',' ')},	/* Nda'nda' -> Bamileke */
+  {HB_TAG('n','o','d',' '),	HB_TAG('N','T','A',' ')},	/* Northern Thai -> Northern Tai */
+/*{HB_TAG('n','o','e',' '),	HB_TAG('N','O','E',' ')},*/	/* Nimadi */
+/*{HB_TAG('n','o','g',' '),	HB_TAG('N','O','G',' ')},*/	/* Nogai */
+/*{HB_TAG('n','o','v',' '),	HB_TAG('N','O','V',' ')},*/	/* Novial */
+  {HB_TAG('n','p','i',' '),	HB_TAG('N','E','P',' ')},	/* Nepali */
+  {HB_TAG('n','p','l',' '),	HB_TAG('N','A','H',' ')},	/* Southeastern Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('n','q','o',' '),	HB_TAG('N','K','O',' ')},	/* N’Ko */
+  {HB_TAG('n','s','k',' '),	HB_TAG('N','A','S',' ')},	/* Naskapi */
+  {HB_TAG('n','s','m',' '),	HB_TAG_NONE	       },	/* Sumi Naga != Northern Sami */
+/*{HB_TAG('n','s','o',' '),	HB_TAG('N','S','O',' ')},*/	/* Northern Sotho */
+  {HB_TAG('n','s','u',' '),	HB_TAG('N','A','H',' ')},	/* Sierra Negra Nahuatl -> Nahuatl */
+  {HB_TAG('n','t','o',' '),	HB_TAG_NONE	       },	/* Ntomba != Esperanto */
+  {HB_TAG('n','u','e',' '),	HB_TAG('B','A','D','0')},	/* Ngundu -> Banda */
+  {HB_TAG('n','u','u',' '),	HB_TAG('B','A','D','0')},	/* Ngbundu -> Banda */
+  {HB_TAG('n','u','z',' '),	HB_TAG('N','A','H',' ')},	/* Tlamacazapa Nahuatl -> Nahuatl */
+  {HB_TAG('n','w','e',' '),	HB_TAG('B','M','L',' ')},	/* Ngwe -> Bamileke */
+  {HB_TAG('n','y','d',' '),	HB_TAG('L','U','H',' ')},	/* Nyore -> Luyia */
+/*{HB_TAG('n','y','m',' '),	HB_TAG('N','Y','M',' ')},*/	/* Nyamwezi */
+  {HB_TAG('n','y','n',' '),	HB_TAG('N','K','L',' ')},	/* Nyankole */
+/*{HB_TAG('n','z','a',' '),	HB_TAG('N','Z','A',' ')},*/	/* Tigon Mbembe -> Mbembe Tigon */
+/*{HB_TAG('o','j','b',' '),	HB_TAG('O','J','B',' ')},*/	/* Northwestern Ojibwa -> Ojibway */
+  {HB_TAG('o','j','c',' '),	HB_TAG('O','J','B',' ')},	/* Central Ojibwa -> Ojibway */
+  {HB_TAG('o','j','g',' '),	HB_TAG('O','J','B',' ')},	/* Eastern Ojibwa -> Ojibway */
+  {HB_TAG('o','j','s',' '),	HB_TAG('O','C','R',' ')},	/* Severn Ojibwa -> Oji-Cree */
+  {HB_TAG('o','j','s',' '),	HB_TAG('O','J','B',' ')},	/* Severn Ojibwa -> Ojibway */
+  {HB_TAG('o','j','w',' '),	HB_TAG('O','J','B',' ')},	/* Western Ojibwa -> Ojibway */
+  {HB_TAG('o','k','d',' '),	HB_TAG('I','J','O',' ')},	/* Okodia -> Ijo */
+  {HB_TAG('o','k','i',' '),	HB_TAG('K','A','L',' ')},	/* Okiek -> Kalenjin */
+  {HB_TAG('o','k','m',' '),	HB_TAG('K','O','H',' ')},	/* Middle Korean (10th-16th cent.) -> Korean Old Hangul */
+  {HB_TAG('o','k','r',' '),	HB_TAG('I','J','O',' ')},	/* Kirike -> Ijo */
+  {HB_TAG('o','n','x',' '),	HB_TAG('C','P','P',' ')},	/* Onin Based Pidgin -> Creoles */
+  {HB_TAG('o','o','r',' '),	HB_TAG('C','P','P',' ')},	/* Oorlams -> Creoles */
+  {HB_TAG('o','r','c',' '),	HB_TAG('O','R','O',' ')},	/* Orma -> Oromo */
+  {HB_TAG('o','r','n',' '),	HB_TAG('M','L','Y',' ')},	/* Orang Kanaq -> Malay */
+  {HB_TAG('o','r','o',' '),	HB_TAG_NONE	       },	/* Orokolo != Oromo */
+  {HB_TAG('o','r','r',' '),	HB_TAG('I','J','O',' ')},	/* Oruma -> Ijo */
+  {HB_TAG('o','r','s',' '),	HB_TAG('M','L','Y',' ')},	/* Orang Seletar -> Malay */
+  {HB_TAG('o','r','y',' '),	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) */
+  {HB_TAG('o','t','w',' '),	HB_TAG('O','J','B',' ')},	/* Ottawa -> Ojibway */
+  {HB_TAG('o','u','a',' '),	HB_TAG('B','B','R',' ')},	/* Tagargrent -> Berber */
+  {HB_TAG('p','a','a',' '),	HB_TAG_NONE	       },	/* Papuan [collection] != Palestinian Aramaic */
+/*{HB_TAG('p','a','g',' '),	HB_TAG('P','A','G',' ')},*/	/* Pangasinan */
+  {HB_TAG('p','a','l',' '),	HB_TAG_NONE	       },	/* Pahlavi != Pali */
+/*{HB_TAG('p','a','m',' '),	HB_TAG('P','A','M',' ')},*/	/* Pampanga -> Pampangan */
+  {HB_TAG('p','a','p',' '),	HB_TAG('P','A','P','0')},	/* Papiamento -> Papiamentu */
+  {HB_TAG('p','a','p',' '),	HB_TAG('C','P','P',' ')},	/* Papiamento -> Creoles */
+  {HB_TAG('p','a','s',' '),	HB_TAG_NONE	       },	/* Papasena != Pashto */
+/*{HB_TAG('p','a','u',' '),	HB_TAG('P','A','U',' ')},*/	/* Palauan */
+  {HB_TAG('p','b','t',' '),	HB_TAG('P','A','S',' ')},	/* Southern Pashto -> Pashto */
+  {HB_TAG('p','b','u',' '),	HB_TAG('P','A','S',' ')},	/* Northern Pashto -> Pashto */
+/*{HB_TAG('p','c','c',' '),	HB_TAG('P','C','C',' ')},*/	/* Bouyei */
+/*{HB_TAG('p','c','d',' '),	HB_TAG('P','C','D',' ')},*/	/* Picard */
+  {HB_TAG('p','c','e',' '),	HB_TAG('P','L','G',' ')},	/* Ruching Palaung -> Palaung */
+  {HB_TAG('p','c','k',' '),	HB_TAG('Q','I','N',' ')},	/* Paite Chin -> Chin */
+  {HB_TAG('p','c','m',' '),	HB_TAG('C','P','P',' ')},	/* Nigerian Pidgin -> Creoles */
+/*{HB_TAG('p','d','c',' '),	HB_TAG('P','D','C',' ')},*/	/* Pennsylvania German */
+  {HB_TAG('p','d','u',' '),	HB_TAG('K','R','N',' ')},	/* Kayan -> Karen */
+  {HB_TAG('p','e','a',' '),	HB_TAG('C','P','P',' ')},	/* Peranakan Indonesian -> Creoles */
+  {HB_TAG('p','e','l',' '),	HB_TAG('M','L','Y',' ')},	/* Pekal -> Malay */
+  {HB_TAG('p','e','s',' '),	HB_TAG('F','A','R',' ')},	/* Iranian Persian -> Persian */
+  {HB_TAG('p','e','y',' '),	HB_TAG('C','P','P',' ')},	/* Petjo -> Creoles */
+  {HB_TAG('p','g','a',' '),	HB_TAG('A','R','A',' ')},	/* Sudanese Creole Arabic -> Arabic */
+  {HB_TAG('p','g','a',' '),	HB_TAG('C','P','P',' ')},	/* Sudanese Creole Arabic -> Creoles */
+/*{HB_TAG('p','h','k',' '),	HB_TAG('P','H','K',' ')},*/	/* Phake */
+  {HB_TAG('p','i','h',' '),	HB_TAG('P','I','H',' ')},	/* Pitcairn-Norfolk -> Norfolk */
+  {HB_TAG('p','i','h',' '),	HB_TAG('C','P','P',' ')},	/* Pitcairn-Norfolk -> Creoles */
+  {HB_TAG('p','i','l',' '),	HB_TAG_NONE	       },	/* Yom != Filipino */
+  {HB_TAG('p','i','s',' '),	HB_TAG('C','P','P',' ')},	/* Pijin -> Creoles */
+  {HB_TAG('p','k','h',' '),	HB_TAG('Q','I','N',' ')},	/* Pankhu -> Chin */
+  {HB_TAG('p','k','o',' '),	HB_TAG('K','A','L',' ')},	/* Pökoot -> Kalenjin */
+  {HB_TAG('p','l','g',' '),	HB_TAG_NONE	       },	/* Pilagá != Palaung */
+  {HB_TAG('p','l','k',' '),	HB_TAG_NONE	       },	/* Kohistani Shina != Polish */
+  {HB_TAG('p','l','l',' '),	HB_TAG('P','L','G',' ')},	/* Shwe Palaung -> Palaung */
+  {HB_TAG('p','l','n',' '),	HB_TAG('C','P','P',' ')},	/* Palenquero -> Creoles */
+  {HB_TAG('p','l','p',' '),	HB_TAG('P','A','P',' ')},	/* Palpa (retired code) */
+  {HB_TAG('p','l','t',' '),	HB_TAG('M','L','G',' ')},	/* Plateau Malagasy -> Malagasy */
+  {HB_TAG('p','m','l',' '),	HB_TAG('C','P','P',' ')},	/* Lingua Franca -> Creoles */
+/*{HB_TAG('p','m','s',' '),	HB_TAG('P','M','S',' ')},*/	/* Piemontese */
+  {HB_TAG('p','m','y',' '),	HB_TAG('C','P','P',' ')},	/* Papuan Malay -> Creoles */
+/*{HB_TAG('p','n','b',' '),	HB_TAG('P','N','B',' ')},*/	/* Western Panjabi */
+  {HB_TAG('p','o','c',' '),	HB_TAG('M','Y','N',' ')},	/* Poqomam -> Mayan */
+  {HB_TAG('p','o','h',' '),	HB_TAG('P','O','H',' ')},	/* Poqomchi' -> Pocomchi */
+  {HB_TAG('p','o','h',' '),	HB_TAG('M','Y','N',' ')},	/* Poqomchi' -> Mayan */
+/*{HB_TAG('p','o','n',' '),	HB_TAG('P','O','N',' ')},*/	/* Pohnpeian */
+  {HB_TAG('p','o','v',' '),	HB_TAG('C','P','P',' ')},	/* Upper Guinea Crioulo -> Creoles */
+  {HB_TAG('p','p','a',' '),	HB_TAG('B','A','G',' ')},	/* Pao (retired code) -> Baghelkhandi */
+  {HB_TAG('p','r','e',' '),	HB_TAG('C','P','P',' ')},	/* Principense -> Creoles */
+/*{HB_TAG('p','r','o',' '),	HB_TAG('P','R','O',' ')},*/	/* Old Provençal (to 1500) -> Provençal / Old Provençal */
+  {HB_TAG('p','r','s',' '),	HB_TAG('D','R','I',' ')},	/* Dari */
+  {HB_TAG('p','r','s',' '),	HB_TAG('F','A','R',' ')},	/* Dari -> Persian */
+  {HB_TAG('p','s','e',' '),	HB_TAG('M','L','Y',' ')},	/* Central Malay -> Malay */
+  {HB_TAG('p','s','t',' '),	HB_TAG('P','A','S',' ')},	/* Central Pashto -> Pashto */
+  {HB_TAG('p','u','b',' '),	HB_TAG('Q','I','N',' ')},	/* Purum -> Chin */
+  {HB_TAG('p','u','z',' '),	HB_TAG('Q','I','N',' ')},	/* Purum Naga (retired code) -> Chin */
+  {HB_TAG('p','w','o',' '),	HB_TAG('P','W','O',' ')},	/* Pwo Western Karen -> Western Pwo Karen */
+  {HB_TAG('p','w','o',' '),	HB_TAG('K','R','N',' ')},	/* Pwo Western Karen -> Karen */
+  {HB_TAG('p','w','w',' '),	HB_TAG('K','R','N',' ')},	/* Pwo Northern Karen -> Karen */
+  {HB_TAG('q','u','b',' '),	HB_TAG('Q','W','H',' ')},	/* Huallaga Huánuco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','u','b',' '),	HB_TAG('Q','U','Z',' ')},	/* Huallaga Huánuco Quechua -> Quechua */
+  {HB_TAG('q','u','c',' '),	HB_TAG('Q','U','C',' ')},	/* K’iche’ */
+  {HB_TAG('q','u','c',' '),	HB_TAG('M','Y','N',' ')},	/* K'iche' -> Mayan */
+  {HB_TAG('q','u','d',' '),	HB_TAG('Q','V','I',' ')},	/* Calderón Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','d',' '),	HB_TAG('Q','U','Z',' ')},	/* Calderón Highland Quichua -> Quechua */
+  {HB_TAG('q','u','f',' '),	HB_TAG('Q','U','Z',' ')},	/* Lambayeque Quechua -> Quechua */
+  {HB_TAG('q','u','g',' '),	HB_TAG('Q','V','I',' ')},	/* Chimborazo Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','g',' '),	HB_TAG('Q','U','Z',' ')},	/* Chimborazo Highland Quichua -> Quechua */
+  {HB_TAG('q','u','h',' '),	HB_TAG('Q','U','H',' ')},	/* South Bolivian Quechua -> Quechua (Bolivia) */
+  {HB_TAG('q','u','h',' '),	HB_TAG('Q','U','Z',' ')},	/* South Bolivian Quechua -> Quechua */
+  {HB_TAG('q','u','k',' '),	HB_TAG('Q','U','Z',' ')},	/* Chachapoyas Quechua -> Quechua */
+  {HB_TAG('q','u','l',' '),	HB_TAG('Q','U','H',' ')},	/* North Bolivian Quechua -> Quechua (Bolivia) */
+  {HB_TAG('q','u','l',' '),	HB_TAG('Q','U','Z',' ')},	/* North Bolivian Quechua -> Quechua */
+  {HB_TAG('q','u','m',' '),	HB_TAG('M','Y','N',' ')},	/* Sipacapense -> Mayan */
+  {HB_TAG('q','u','p',' '),	HB_TAG('Q','V','I',' ')},	/* Southern Pastaza Quechua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','p',' '),	HB_TAG('Q','U','Z',' ')},	/* Southern Pastaza Quechua -> Quechua */
+  {HB_TAG('q','u','r',' '),	HB_TAG('Q','W','H',' ')},	/* Yanahuanca Pasco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','u','r',' '),	HB_TAG('Q','U','Z',' ')},	/* Yanahuanca Pasco Quechua -> Quechua */
+  {HB_TAG('q','u','s',' '),	HB_TAG('Q','U','H',' ')},	/* Santiago del Estero Quichua -> Quechua (Bolivia) */
+  {HB_TAG('q','u','s',' '),	HB_TAG('Q','U','Z',' ')},	/* Santiago del Estero Quichua -> Quechua */
+  {HB_TAG('q','u','v',' '),	HB_TAG('M','Y','N',' ')},	/* Sacapulteco -> Mayan */
+  {HB_TAG('q','u','w',' '),	HB_TAG('Q','V','I',' ')},	/* Tena Lowland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','w',' '),	HB_TAG('Q','U','Z',' ')},	/* Tena Lowland Quichua -> Quechua */
+  {HB_TAG('q','u','x',' '),	HB_TAG('Q','W','H',' ')},	/* Yauyos Quechua -> Quechua (Peru) */
+  {HB_TAG('q','u','x',' '),	HB_TAG('Q','U','Z',' ')},	/* Yauyos Quechua -> Quechua */
+  {HB_TAG('q','u','y',' '),	HB_TAG('Q','U','Z',' ')},	/* Ayacucho Quechua -> Quechua */
+/*{HB_TAG('q','u','z',' '),	HB_TAG('Q','U','Z',' ')},*/	/* Cusco Quechua -> Quechua */
+  {HB_TAG('q','v','a',' '),	HB_TAG('Q','W','H',' ')},	/* Ambo-Pasco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','a',' '),	HB_TAG('Q','U','Z',' ')},	/* Ambo-Pasco Quechua -> Quechua */
+  {HB_TAG('q','v','c',' '),	HB_TAG('Q','U','Z',' ')},	/* Cajamarca Quechua -> Quechua */
+  {HB_TAG('q','v','e',' '),	HB_TAG('Q','U','Z',' ')},	/* Eastern Apurímac Quechua -> Quechua */
+  {HB_TAG('q','v','h',' '),	HB_TAG('Q','W','H',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','h',' '),	HB_TAG('Q','U','Z',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */
+  {HB_TAG('q','v','i',' '),	HB_TAG('Q','V','I',' ')},	/* Imbabura Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','i',' '),	HB_TAG('Q','U','Z',' ')},	/* Imbabura Highland Quichua -> Quechua */
+  {HB_TAG('q','v','j',' '),	HB_TAG('Q','V','I',' ')},	/* Loja Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','j',' '),	HB_TAG('Q','U','Z',' ')},	/* Loja Highland Quichua -> Quechua */
+  {HB_TAG('q','v','l',' '),	HB_TAG('Q','W','H',' ')},	/* Cajatambo North Lima Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','l',' '),	HB_TAG('Q','U','Z',' ')},	/* Cajatambo North Lima Quechua -> Quechua */
+  {HB_TAG('q','v','m',' '),	HB_TAG('Q','W','H',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','m',' '),	HB_TAG('Q','U','Z',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua */
+  {HB_TAG('q','v','n',' '),	HB_TAG('Q','W','H',' ')},	/* North Junín Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','n',' '),	HB_TAG('Q','U','Z',' ')},	/* North Junín Quechua -> Quechua */
+  {HB_TAG('q','v','o',' '),	HB_TAG('Q','V','I',' ')},	/* Napo Lowland Quechua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','o',' '),	HB_TAG('Q','U','Z',' ')},	/* Napo Lowland Quechua -> Quechua */
+  {HB_TAG('q','v','p',' '),	HB_TAG('Q','W','H',' ')},	/* Pacaraos Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','p',' '),	HB_TAG('Q','U','Z',' ')},	/* Pacaraos Quechua -> Quechua */
+  {HB_TAG('q','v','s',' '),	HB_TAG('Q','U','Z',' ')},	/* San Martín Quechua -> Quechua */
+  {HB_TAG('q','v','w',' '),	HB_TAG('Q','W','H',' ')},	/* Huaylla Wanca Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','w',' '),	HB_TAG('Q','U','Z',' ')},	/* Huaylla Wanca Quechua -> Quechua */
+  {HB_TAG('q','v','z',' '),	HB_TAG('Q','V','I',' ')},	/* Northern Pastaza Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','z',' '),	HB_TAG('Q','U','Z',' ')},	/* Northern Pastaza Quichua -> Quechua */
+  {HB_TAG('q','w','a',' '),	HB_TAG('Q','W','H',' ')},	/* Corongo Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','w','a',' '),	HB_TAG('Q','U','Z',' ')},	/* Corongo Ancash Quechua -> Quechua */
+  {HB_TAG('q','w','c',' '),	HB_TAG('Q','U','Z',' ')},	/* Classical Quechua -> Quechua */
+  {HB_TAG('q','w','h',' '),	HB_TAG('Q','W','H',' ')},	/* Huaylas Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','w','h',' '),	HB_TAG('Q','U','Z',' ')},	/* Huaylas Ancash Quechua -> Quechua */
+  {HB_TAG('q','w','s',' '),	HB_TAG('Q','W','H',' ')},	/* Sihuas Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','w','s',' '),	HB_TAG('Q','U','Z',' ')},	/* Sihuas Ancash Quechua -> Quechua */
+  {HB_TAG('q','w','t',' '),	HB_TAG('A','T','H',' ')},	/* Kwalhioqua-Tlatskanai -> Athapaskan */
+  {HB_TAG('q','x','a',' '),	HB_TAG('Q','W','H',' ')},	/* Chiquián Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','a',' '),	HB_TAG('Q','U','Z',' ')},	/* Chiquián Ancash Quechua -> Quechua */
+  {HB_TAG('q','x','c',' '),	HB_TAG('Q','W','H',' ')},	/* Chincha Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','c',' '),	HB_TAG('Q','U','Z',' ')},	/* Chincha Quechua -> Quechua */
+  {HB_TAG('q','x','h',' '),	HB_TAG('Q','W','H',' ')},	/* Panao Huánuco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','h',' '),	HB_TAG('Q','U','Z',' ')},	/* Panao Huánuco Quechua -> Quechua */
+  {HB_TAG('q','x','l',' '),	HB_TAG('Q','V','I',' ')},	/* Salasaca Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','x','l',' '),	HB_TAG('Q','U','Z',' ')},	/* Salasaca Highland Quichua -> Quechua */
+  {HB_TAG('q','x','n',' '),	HB_TAG('Q','W','H',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','n',' '),	HB_TAG('Q','U','Z',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua */
+  {HB_TAG('q','x','o',' '),	HB_TAG('Q','W','H',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','o',' '),	HB_TAG('Q','U','Z',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua */
+  {HB_TAG('q','x','p',' '),	HB_TAG('Q','U','Z',' ')},	/* Puno Quechua -> Quechua */
+  {HB_TAG('q','x','r',' '),	HB_TAG('Q','V','I',' ')},	/* Cañar Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','x','r',' '),	HB_TAG('Q','U','Z',' ')},	/* Cañar Highland Quichua -> Quechua */
+  {HB_TAG('q','x','t',' '),	HB_TAG('Q','W','H',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','t',' '),	HB_TAG('Q','U','Z',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua */
+  {HB_TAG('q','x','u',' '),	HB_TAG('Q','U','Z',' ')},	/* Arequipa-La Unión Quechua -> Quechua */
+  {HB_TAG('q','x','w',' '),	HB_TAG('Q','W','H',' ')},	/* Jauja Wanca Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','w',' '),	HB_TAG('Q','U','Z',' ')},	/* Jauja Wanca Quechua -> Quechua */
+  {HB_TAG('r','a','g',' '),	HB_TAG('L','U','H',' ')},	/* Logooli -> Luyia */
+/*{HB_TAG('r','a','j',' '),	HB_TAG('R','A','J',' ')},*/	/* Rajasthani [macrolanguage] */
+  {HB_TAG('r','a','l',' '),	HB_TAG('Q','I','N',' ')},	/* Ralte -> Chin */
+/*{HB_TAG('r','a','r',' '),	HB_TAG('R','A','R',' ')},*/	/* Rarotongan */
+  {HB_TAG('r','b','b',' '),	HB_TAG('P','L','G',' ')},	/* Rumai Palaung -> Palaung */
+  {HB_TAG('r','b','l',' '),	HB_TAG('B','I','K',' ')},	/* Miraya Bikol -> Bikol */
+  {HB_TAG('r','c','f',' '),	HB_TAG('C','P','P',' ')},	/* Réunion Creole French -> Creoles */
+/*{HB_TAG('r','e','j',' '),	HB_TAG('R','E','J',' ')},*/	/* Rejang */
+/*{HB_TAG('r','h','g',' '),	HB_TAG('R','H','G',' ')},*/	/* Rohingya */
+/*{HB_TAG('r','i','a',' '),	HB_TAG('R','I','A',' ')},*/	/* Riang (India) */
+  {HB_TAG('r','i','f',' '),	HB_TAG('R','I','F',' ')},	/* Tarifit */
+  {HB_TAG('r','i','f',' '),	HB_TAG('B','B','R',' ')},	/* Tarifit -> Berber */
+/*{HB_TAG('r','i','t',' '),	HB_TAG('R','I','T',' ')},*/	/* Ritharrngu -> Ritarungo */
+  {HB_TAG('r','k','i',' '),	HB_TAG('A','R','K',' ')},	/* Rakhine */
+/*{HB_TAG('r','k','w',' '),	HB_TAG('R','K','W',' ')},*/	/* Arakwal */
+  {HB_TAG('r','m','c',' '),	HB_TAG('R','O','Y',' ')},	/* Carpathian Romani -> Romany */
+  {HB_TAG('r','m','f',' '),	HB_TAG('R','O','Y',' ')},	/* Kalo Finnish Romani -> Romany */
+  {HB_TAG('r','m','l',' '),	HB_TAG('R','O','Y',' ')},	/* Baltic Romani -> Romany */
+  {HB_TAG('r','m','n',' '),	HB_TAG('R','O','Y',' ')},	/* Balkan Romani -> Romany */
+  {HB_TAG('r','m','o',' '),	HB_TAG('R','O','Y',' ')},	/* Sinte Romani -> Romany */
+  {HB_TAG('r','m','s',' '),	HB_TAG_NONE	       },	/* Romanian Sign Language != Romansh */
+  {HB_TAG('r','m','w',' '),	HB_TAG('R','O','Y',' ')},	/* Welsh Romani -> Romany */
+  {HB_TAG('r','m','y',' '),	HB_TAG('R','M','Y',' ')},	/* Vlax Romani */
+  {HB_TAG('r','m','y',' '),	HB_TAG('R','O','Y',' ')},	/* Vlax Romani -> Romany */
+  {HB_TAG('r','m','z',' '),	HB_TAG('A','R','K',' ')},	/* Marma -> Rakhine */
+  {HB_TAG('r','o','m',' '),	HB_TAG('R','O','Y',' ')},	/* Romany [macrolanguage] */
+  {HB_TAG('r','o','p',' '),	HB_TAG('C','P','P',' ')},	/* Kriol -> Creoles */
+  {HB_TAG('r','t','c',' '),	HB_TAG('Q','I','N',' ')},	/* Rungtu Chin -> Chin */
+/*{HB_TAG('r','t','m',' '),	HB_TAG('R','T','M',' ')},*/	/* Rotuman */
+  {HB_TAG('r','u','e',' '),	HB_TAG('R','S','Y',' ')},	/* Rusyn */
+/*{HB_TAG('r','u','p',' '),	HB_TAG('R','U','P',' ')},*/	/* Aromanian */
+  {HB_TAG('r','w','r',' '),	HB_TAG('M','A','W',' ')},	/* Marwari (India) */
+  {HB_TAG('s','a','d',' '),	HB_TAG_NONE	       },	/* Sandawe != Sadri */
+  {HB_TAG('s','a','h',' '),	HB_TAG('Y','A','K',' ')},	/* Yakut -> Sakha */
+  {HB_TAG('s','a','m',' '),	HB_TAG('P','A','A',' ')},	/* Samaritan Aramaic -> Palestinian Aramaic */
+/*{HB_TAG('s','a','s',' '),	HB_TAG('S','A','S',' ')},*/	/* Sasak */
+/*{HB_TAG('s','a','t',' '),	HB_TAG('S','A','T',' ')},*/	/* Santali */
+  {HB_TAG('s','a','y',' '),	HB_TAG_NONE	       },	/* Saya != Sayisi */
+  {HB_TAG('s','c','f',' '),	HB_TAG('C','P','P',' ')},	/* San Miguel Creole French -> Creoles */
+  {HB_TAG('s','c','h',' '),	HB_TAG('Q','I','N',' ')},	/* Sakachep -> Chin */
+  {HB_TAG('s','c','i',' '),	HB_TAG('C','P','P',' ')},	/* Sri Lankan Creole Malay -> Creoles */
+  {HB_TAG('s','c','k',' '),	HB_TAG('S','A','D',' ')},	/* Sadri */
+/*{HB_TAG('s','c','n',' '),	HB_TAG('S','C','N',' ')},*/	/* Sicilian */
+/*{HB_TAG('s','c','o',' '),	HB_TAG('S','C','O',' ')},*/	/* Scots */
+  {HB_TAG('s','c','s',' '),	HB_TAG('S','C','S',' ')},	/* North Slavey */
+  {HB_TAG('s','c','s',' '),	HB_TAG('S','L','A',' ')},	/* North Slavey -> Slavey */
+  {HB_TAG('s','c','s',' '),	HB_TAG('A','T','H',' ')},	/* North Slavey -> Athapaskan */
+  {HB_TAG('s','d','c',' '),	HB_TAG('S','R','D',' ')},	/* Sassarese Sardinian -> Sardinian */
+  {HB_TAG('s','d','h',' '),	HB_TAG('K','U','R',' ')},	/* Southern Kurdish -> Kurdish */
+  {HB_TAG('s','d','n',' '),	HB_TAG('S','R','D',' ')},	/* Gallurese Sardinian -> Sardinian */
+  {HB_TAG('s','d','s',' '),	HB_TAG('B','B','R',' ')},	/* Sened -> Berber */
+  {HB_TAG('s','e','h',' '),	HB_TAG('S','N','A',' ')},	/* Sena */
+  {HB_TAG('s','e','k',' '),	HB_TAG('A','T','H',' ')},	/* Sekani -> Athapaskan */
+/*{HB_TAG('s','e','l',' '),	HB_TAG('S','E','L',' ')},*/	/* Selkup */
+  {HB_TAG('s','e','z',' '),	HB_TAG('Q','I','N',' ')},	/* Senthang Chin -> Chin */
+  {HB_TAG('s','f','m',' '),	HB_TAG('S','F','M',' ')},	/* Small Flowery Miao */
+  {HB_TAG('s','f','m',' '),	HB_TAG('H','M','N',' ')},	/* Small Flowery Miao -> Hmong */
+/*{HB_TAG('s','g','a',' '),	HB_TAG('S','G','A',' ')},*/	/* Old Irish (to 900) */
+  {HB_TAG('s','g','c',' '),	HB_TAG('K','A','L',' ')},	/* Kipsigis -> Kalenjin */
+  {HB_TAG('s','g','o',' '),	HB_TAG_NONE	       },	/* Songa (retired code) != Sango */
+/*{HB_TAG('s','g','s',' '),	HB_TAG('S','G','S',' ')},*/	/* Samogitian */
+  {HB_TAG('s','g','w',' '),	HB_TAG('C','H','G',' ')},	/* Sebat Bet Gurage -> Chaha Gurage */
+  {HB_TAG('s','h','i',' '),	HB_TAG('S','H','I',' ')},	/* Tachelhit */
+  {HB_TAG('s','h','i',' '),	HB_TAG('B','B','R',' ')},	/* Tachelhit -> Berber */
+  {HB_TAG('s','h','l',' '),	HB_TAG('Q','I','N',' ')},	/* Shendu -> Chin */
+/*{HB_TAG('s','h','n',' '),	HB_TAG('S','H','N',' ')},*/	/* Shan */
+  {HB_TAG('s','h','u',' '),	HB_TAG('A','R','A',' ')},	/* Chadian Arabic -> Arabic */
+  {HB_TAG('s','h','y',' '),	HB_TAG('B','B','R',' ')},	/* Tachawit -> Berber */
+  {HB_TAG('s','i','b',' '),	HB_TAG_NONE	       },	/* Sebop != Sibe */
+/*{HB_TAG('s','i','d',' '),	HB_TAG('S','I','D',' ')},*/	/* Sidamo */
+  {HB_TAG('s','i','g',' '),	HB_TAG_NONE	       },	/* Paasaal != Silte Gurage */
+  {HB_TAG('s','i','z',' '),	HB_TAG('B','B','R',' ')},	/* Siwi -> Berber */
+  {HB_TAG('s','j','d',' '),	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
+  {HB_TAG('s','j','o',' '),	HB_TAG('S','I','B',' ')},	/* Xibe -> Sibe */
+  {HB_TAG('s','j','s',' '),	HB_TAG('B','B','R',' ')},	/* Senhaja De Srair -> Berber */
+  {HB_TAG('s','k','g',' '),	HB_TAG('M','L','G',' ')},	/* Sakalava Malagasy -> Malagasy */
+  {HB_TAG('s','k','r',' '),	HB_TAG('S','R','K',' ')},	/* Saraiki */
+  {HB_TAG('s','k','s',' '),	HB_TAG_NONE	       },	/* Maia != Skolt Sami */
+  {HB_TAG('s','k','w',' '),	HB_TAG('C','P','P',' ')},	/* Skepi Creole Dutch -> Creoles */
+  {HB_TAG('s','k','y',' '),	HB_TAG_NONE	       },	/* Sikaiana != Slovak */
+  {HB_TAG('s','l','a',' '),	HB_TAG_NONE	       },	/* Slavic [collection] != Slavey */
+  {HB_TAG('s','m','a',' '),	HB_TAG('S','S','M',' ')},	/* Southern Sami */
+  {HB_TAG('s','m','d',' '),	HB_TAG('M','B','N',' ')},	/* Sama (retired code) -> Mbundu */
+  {HB_TAG('s','m','j',' '),	HB_TAG('L','S','M',' ')},	/* Lule Sami */
+  {HB_TAG('s','m','l',' '),	HB_TAG_NONE	       },	/* Central Sama != Somali */
+  {HB_TAG('s','m','n',' '),	HB_TAG('I','S','M',' ')},	/* Inari Sami */
+  {HB_TAG('s','m','s',' '),	HB_TAG('S','K','S',' ')},	/* Skolt Sami */
+  {HB_TAG('s','m','t',' '),	HB_TAG('Q','I','N',' ')},	/* Simte -> Chin */
+  {HB_TAG('s','n','b',' '),	HB_TAG('I','B','A',' ')},	/* Sebuyau (retired code) -> Iban */
+  {HB_TAG('s','n','h',' '),	HB_TAG_NONE	       },	/* Shinabo (retired code) != Sinhala (Sinhalese) */
+/*{HB_TAG('s','n','k',' '),	HB_TAG('S','N','K',' ')},*/	/* Soninke */
+  {HB_TAG('s','o','g',' '),	HB_TAG_NONE	       },	/* Sogdian != Sodo Gurage */
+/*{HB_TAG('s','o','p',' '),	HB_TAG('S','O','P',' ')},*/	/* Songe */
+  {HB_TAG('s','p','v',' '),	HB_TAG('O','R','I',' ')},	/* Sambalpuri -> Odia (formerly Oriya) */
+  {HB_TAG('s','p','y',' '),	HB_TAG('K','A','L',' ')},	/* Sabaot -> Kalenjin */
+  {HB_TAG('s','r','b',' '),	HB_TAG_NONE	       },	/* Sora != Serbian */
+  {HB_TAG('s','r','c',' '),	HB_TAG('S','R','D',' ')},	/* Logudorese Sardinian -> Sardinian */
+  {HB_TAG('s','r','k',' '),	HB_TAG_NONE	       },	/* Serudung Murut != Saraiki */
+  {HB_TAG('s','r','m',' '),	HB_TAG('C','P','P',' ')},	/* Saramaccan -> Creoles */
+  {HB_TAG('s','r','n',' '),	HB_TAG('C','P','P',' ')},	/* Sranan Tongo -> Creoles */
+  {HB_TAG('s','r','o',' '),	HB_TAG('S','R','D',' ')},	/* Campidanese Sardinian -> Sardinian */
+/*{HB_TAG('s','r','r',' '),	HB_TAG('S','R','R',' ')},*/	/* Serer */
+  {HB_TAG('s','r','s',' '),	HB_TAG('A','T','H',' ')},	/* Sarsi -> Athapaskan */
+  {HB_TAG('s','s','h',' '),	HB_TAG('A','R','A',' ')},	/* Shihhi Arabic -> Arabic */
+  {HB_TAG('s','s','l',' '),	HB_TAG_NONE	       },	/* Western Sisaala != South Slavey */
+  {HB_TAG('s','s','m',' '),	HB_TAG_NONE	       },	/* Semnam != Southern Sami */
+  {HB_TAG('s','t','a',' '),	HB_TAG('C','P','P',' ')},	/* Settla -> Creoles */
+/*{HB_TAG('s','t','q',' '),	HB_TAG('S','T','Q',' ')},*/	/* Saterfriesisch -> Saterland Frisian */
+  {HB_TAG('s','t','v',' '),	HB_TAG('S','I','G',' ')},	/* Silt'e -> Silte Gurage */
+/*{HB_TAG('s','u','k',' '),	HB_TAG('S','U','K',' ')},*/	/* Sukuma */
+  {HB_TAG('s','u','q',' '),	HB_TAG('S','U','R',' ')},	/* Suri */
+  {HB_TAG('s','u','r',' '),	HB_TAG_NONE	       },	/* Mwaghavul != Suri */
+/*{HB_TAG('s','v','a',' '),	HB_TAG('S','V','A',' ')},*/	/* Svan */
+  {HB_TAG('s','v','c',' '),	HB_TAG('C','P','P',' ')},	/* Vincentian Creole English -> Creoles */
+  {HB_TAG('s','v','e',' '),	HB_TAG_NONE	       },	/* Serili != Swedish */
+  {HB_TAG('s','w','b',' '),	HB_TAG('C','M','R',' ')},	/* Maore Comorian -> Comorian */
+  {HB_TAG('s','w','c',' '),	HB_TAG('S','W','K',' ')},	/* Congo Swahili -> Swahili */
+  {HB_TAG('s','w','h',' '),	HB_TAG('S','W','K',' ')},	/* Swahili */
+  {HB_TAG('s','w','k',' '),	HB_TAG_NONE	       },	/* Malawi Sena != Swahili */
+  {HB_TAG('s','w','n',' '),	HB_TAG('B','B','R',' ')},	/* Sawknah -> Berber */
+  {HB_TAG('s','w','v',' '),	HB_TAG('M','A','W',' ')},	/* Shekhawati -> Marwari */
+/*{HB_TAG('s','x','u',' '),	HB_TAG('S','X','U',' ')},*/	/* Upper Saxon */
+  {HB_TAG('s','y','c',' '),	HB_TAG('S','Y','R',' ')},	/* Classical Syriac -> Syriac */
+/*{HB_TAG('s','y','l',' '),	HB_TAG('S','Y','L',' ')},*/	/* Sylheti */
+/*{HB_TAG('s','y','r',' '),	HB_TAG('S','Y','R',' ')},*/	/* Syriac [macrolanguage] */
+/*{HB_TAG('s','z','l',' '),	HB_TAG('S','Z','L',' ')},*/	/* Silesian */
+  {HB_TAG('t','a','a',' '),	HB_TAG('A','T','H',' ')},	/* Lower Tanana -> Athapaskan */
+/*{HB_TAG('t','a','b',' '),	HB_TAG('T','A','B',' ')},*/	/* Tabassaran -> Tabasaran */
+  {HB_TAG('t','a','j',' '),	HB_TAG_NONE	       },	/* Eastern Tamang != Tajiki */
+  {HB_TAG('t','a','q',' '),	HB_TAG('T','M','H',' ')},	/* Tamasheq -> Tamashek */
+  {HB_TAG('t','a','q',' '),	HB_TAG('B','B','R',' ')},	/* Tamasheq -> Berber */
+  {HB_TAG('t','a','s',' '),	HB_TAG('C','P','P',' ')},	/* Tay Boi -> Creoles */
+  {HB_TAG('t','a','u',' '),	HB_TAG('A','T','H',' ')},	/* Upper Tanana -> Athapaskan */
+  {HB_TAG('t','c','b',' '),	HB_TAG('A','T','H',' ')},	/* Tanacross -> Athapaskan */
+  {HB_TAG('t','c','e',' '),	HB_TAG('A','T','H',' ')},	/* Southern Tutchone -> Athapaskan */
+  {HB_TAG('t','c','h',' '),	HB_TAG('C','P','P',' ')},	/* Turks And Caicos Creole English -> Creoles */
+  {HB_TAG('t','c','p',' '),	HB_TAG('Q','I','N',' ')},	/* Tawr Chin -> Chin */
+  {HB_TAG('t','c','s',' '),	HB_TAG('C','P','P',' ')},	/* Torres Strait Creole -> Creoles */
+  {HB_TAG('t','c','y',' '),	HB_TAG('T','U','L',' ')},	/* Tulu -> Tumbuka */
+  {HB_TAG('t','c','z',' '),	HB_TAG('Q','I','N',' ')},	/* Thado Chin -> Chin */
+/*{HB_TAG('t','d','d',' '),	HB_TAG('T','D','D',' ')},*/	/* Tai Nüa -> Dehong Dai */
+  {HB_TAG('t','d','x',' '),	HB_TAG('M','L','G',' ')},	/* Tandroy-Mahafaly Malagasy -> Malagasy */
+  {HB_TAG('t','e','c',' '),	HB_TAG('K','A','L',' ')},	/* Terik -> Kalenjin */
+  {HB_TAG('t','e','m',' '),	HB_TAG('T','M','N',' ')},	/* Timne -> Temne */
+/*{HB_TAG('t','e','t',' '),	HB_TAG('T','E','T',' ')},*/	/* Tetum */
+  {HB_TAG('t','e','z',' '),	HB_TAG('B','B','R',' ')},	/* Tetserret -> Berber */
+  {HB_TAG('t','f','n',' '),	HB_TAG('A','T','H',' ')},	/* Tanaina -> Athapaskan */
+  {HB_TAG('t','g','h',' '),	HB_TAG('C','P','P',' ')},	/* Tobagonian Creole English -> Creoles */
+  {HB_TAG('t','g','j',' '),	HB_TAG('N','I','S',' ')},	/* Tagin -> Nisi */
+  {HB_TAG('t','g','n',' '),	HB_TAG_NONE	       },	/* Tandaganon != Tongan */
+  {HB_TAG('t','g','r',' '),	HB_TAG_NONE	       },	/* Tareng != Tigre */
+  {HB_TAG('t','g','x',' '),	HB_TAG('A','T','H',' ')},	/* Tagish -> Athapaskan */
+  {HB_TAG('t','g','y',' '),	HB_TAG_NONE	       },	/* Togoyo != Tigrinya */
+  {HB_TAG('t','h','t',' '),	HB_TAG('A','T','H',' ')},	/* Tahltan -> Athapaskan */
+  {HB_TAG('t','h','v',' '),	HB_TAG('T','M','H',' ')},	/* Tahaggart Tamahaq -> Tamashek */
+  {HB_TAG('t','h','v',' '),	HB_TAG('B','B','R',' ')},	/* Tahaggart Tamahaq -> Berber */
+  {HB_TAG('t','h','z',' '),	HB_TAG('T','M','H',' ')},	/* Tayart Tamajeq -> Tamashek */
+  {HB_TAG('t','h','z',' '),	HB_TAG('B','B','R',' ')},	/* Tayart Tamajeq -> Berber */
+  {HB_TAG('t','i','a',' '),	HB_TAG('B','B','R',' ')},	/* Tidikelt Tamazight -> Berber */
+  {HB_TAG('t','i','g',' '),	HB_TAG('T','G','R',' ')},	/* Tigre */
+/*{HB_TAG('t','i','v',' '),	HB_TAG('T','I','V',' ')},*/	/* Tiv */
+/*{HB_TAG('t','j','l',' '),	HB_TAG('T','J','L',' ')},*/	/* Tai Laing */
+  {HB_TAG('t','j','o',' '),	HB_TAG('B','B','R',' ')},	/* Temacine Tamazight -> Berber */
+  {HB_TAG('t','k','g',' '),	HB_TAG('M','L','G',' ')},	/* Tesaka Malagasy -> Malagasy */
+  {HB_TAG('t','k','m',' '),	HB_TAG_NONE	       },	/* Takelma != Turkmen */
+/*{HB_TAG('t','l','i',' '),	HB_TAG('T','L','I',' ')},*/	/* Tlingit */
+  {HB_TAG('t','m','g',' '),	HB_TAG('C','P','P',' ')},	/* Ternateño -> Creoles */
+  {HB_TAG('t','m','h',' '),	HB_TAG('T','M','H',' ')},	/* Tamashek [macrolanguage] */
+  {HB_TAG('t','m','h',' '),	HB_TAG('B','B','R',' ')},	/* Tamashek [macrolanguage] -> Berber */
+  {HB_TAG('t','m','n',' '),	HB_TAG_NONE	       },	/* Taman (Indonesia) != Temne */
+  {HB_TAG('t','m','w',' '),	HB_TAG('M','L','Y',' ')},	/* Temuan -> Malay */
+  {HB_TAG('t','n','a',' '),	HB_TAG_NONE	       },	/* Tacana != Tswana */
+  {HB_TAG('t','n','e',' '),	HB_TAG_NONE	       },	/* Tinoc Kallahan (retired code) != Tundra Enets */
+  {HB_TAG('t','n','f',' '),	HB_TAG('D','R','I',' ')},	/* Tangshewi (retired code) -> Dari */
+  {HB_TAG('t','n','f',' '),	HB_TAG('F','A','R',' ')},	/* Tangshewi (retired code) -> Persian */
+  {HB_TAG('t','n','g',' '),	HB_TAG_NONE	       },	/* Tobanga != Tonga */
+  {HB_TAG('t','o','d',' '),	HB_TAG('T','O','D','0')},	/* Toma */
+  {HB_TAG('t','o','i',' '),	HB_TAG('T','N','G',' ')},	/* Tonga (Zambia) */
+  {HB_TAG('t','o','j',' '),	HB_TAG('M','Y','N',' ')},	/* Tojolabal -> Mayan */
+  {HB_TAG('t','o','l',' '),	HB_TAG('A','T','H',' ')},	/* Tolowa -> Athapaskan */
+  {HB_TAG('t','o','r',' '),	HB_TAG('B','A','D','0')},	/* Togbo-Vara Banda -> Banda */
+  {HB_TAG('t','p','i',' '),	HB_TAG('T','P','I',' ')},	/* Tok Pisin */
+  {HB_TAG('t','p','i',' '),	HB_TAG('C','P','P',' ')},	/* Tok Pisin -> Creoles */
+  {HB_TAG('t','r','f',' '),	HB_TAG('C','P','P',' ')},	/* Trinidadian Creole English -> Creoles */
+  {HB_TAG('t','r','k',' '),	HB_TAG_NONE	       },	/* Turkic [collection] != Turkish */
+  {HB_TAG('t','r','u',' '),	HB_TAG('T','U','A',' ')},	/* Turoyo -> Turoyo Aramaic */
+  {HB_TAG('t','r','u',' '),	HB_TAG('S','Y','R',' ')},	/* Turoyo -> Syriac */
+  {HB_TAG('t','s','g',' '),	HB_TAG_NONE	       },	/* Tausug != Tsonga */
+/*{HB_TAG('t','s','j',' '),	HB_TAG('T','S','J',' ')},*/	/* Tshangla */
+  {HB_TAG('t','t','c',' '),	HB_TAG('M','Y','N',' ')},	/* Tektiteko -> Mayan */
+  {HB_TAG('t','t','m',' '),	HB_TAG('A','T','H',' ')},	/* Northern Tutchone -> Athapaskan */
+  {HB_TAG('t','t','q',' '),	HB_TAG('T','M','H',' ')},	/* Tawallammat Tamajaq -> Tamashek */
+  {HB_TAG('t','t','q',' '),	HB_TAG('B','B','R',' ')},	/* Tawallammat Tamajaq -> Berber */
+  {HB_TAG('t','u','a',' '),	HB_TAG_NONE	       },	/* Wiarumus != Turoyo Aramaic */
+  {HB_TAG('t','u','l',' '),	HB_TAG_NONE	       },	/* Tula != Tumbuka */
+/*{HB_TAG('t','u','m',' '),	HB_TAG('T','U','M',' ')},*/	/* Tumbuka -> Tulu */
+  {HB_TAG('t','u','u',' '),	HB_TAG('A','T','H',' ')},	/* Tututni -> Athapaskan */
+  {HB_TAG('t','u','v',' '),	HB_TAG_NONE	       },	/* Turkana != Tuvin */
+  {HB_TAG('t','u','y',' '),	HB_TAG('K','A','L',' ')},	/* Tugen -> Kalenjin */
+/*{HB_TAG('t','v','l',' '),	HB_TAG('T','V','L',' ')},*/	/* Tuvalu */
+  {HB_TAG('t','v','y',' '),	HB_TAG('C','P','P',' ')},	/* Timor Pidgin -> Creoles */
+  {HB_TAG('t','x','c',' '),	HB_TAG('A','T','H',' ')},	/* Tsetsaut -> Athapaskan */
+  {HB_TAG('t','x','y',' '),	HB_TAG('M','L','G',' ')},	/* Tanosy Malagasy -> Malagasy */
+  {HB_TAG('t','y','v',' '),	HB_TAG('T','U','V',' ')},	/* Tuvinian -> Tuvin */
+/*{HB_TAG('t','y','z',' '),	HB_TAG('T','Y','Z',' ')},*/	/* Tày */
+  {HB_TAG('t','z','h',' '),	HB_TAG('M','Y','N',' ')},	/* Tzeltal -> Mayan */
+  {HB_TAG('t','z','j',' '),	HB_TAG('M','Y','N',' ')},	/* Tz'utujil -> Mayan */
+  {HB_TAG('t','z','m',' '),	HB_TAG('T','Z','M',' ')},	/* Central Atlas Tamazight -> Tamazight */
+  {HB_TAG('t','z','m',' '),	HB_TAG('B','B','R',' ')},	/* Central Atlas Tamazight -> Berber */
+  {HB_TAG('t','z','o',' '),	HB_TAG('T','Z','O',' ')},	/* Tzotzil */
+  {HB_TAG('t','z','o',' '),	HB_TAG('M','Y','N',' ')},	/* Tzotzil -> Mayan */
+  {HB_TAG('u','b','l',' '),	HB_TAG('B','I','K',' ')},	/* Buhi'non Bikol -> Bikol */
+/*{HB_TAG('u','d','m',' '),	HB_TAG('U','D','M',' ')},*/	/* Udmurt */
+  {HB_TAG('u','k','i',' '),	HB_TAG('K','U','I',' ')},	/* Kui (India) */
+  {HB_TAG('u','l','n',' '),	HB_TAG('C','P','P',' ')},	/* Unserdeutsch -> Creoles */
+/*{HB_TAG('u','m','b',' '),	HB_TAG('U','M','B',' ')},*/	/* Umbundu */
+  {HB_TAG('u','n','r',' '),	HB_TAG('M','U','N',' ')},	/* Mundari */
+  {HB_TAG('u','r','k',' '),	HB_TAG('M','L','Y',' ')},	/* Urak Lawoi' -> Malay */
+  {HB_TAG('u','s','p',' '),	HB_TAG('M','Y','N',' ')},	/* Uspanteco -> Mayan */
+  {HB_TAG('u','z','n',' '),	HB_TAG('U','Z','B',' ')},	/* Northern Uzbek -> Uzbek */
+  {HB_TAG('u','z','s',' '),	HB_TAG('U','Z','B',' ')},	/* Southern Uzbek -> Uzbek */
+  {HB_TAG('v','a','p',' '),	HB_TAG('Q','I','N',' ')},	/* Vaiphei -> Chin */
+/*{HB_TAG('v','e','c',' '),	HB_TAG('V','E','C',' ')},*/	/* Venetian */
+  {HB_TAG('v','i','c',' '),	HB_TAG('C','P','P',' ')},	/* Virgin Islands Creole English -> Creoles */
+  {HB_TAG('v','i','t',' '),	HB_TAG_NONE	       },	/* Viti != Vietnamese */
+  {HB_TAG('v','k','k',' '),	HB_TAG('M','L','Y',' ')},	/* Kaur -> Malay */
+  {HB_TAG('v','k','p',' '),	HB_TAG('C','P','P',' ')},	/* Korlai Creole Portuguese -> Creoles */
+  {HB_TAG('v','k','t',' '),	HB_TAG('M','L','Y',' ')},	/* Tenggarong Kutai Malay -> Malay */
+  {HB_TAG('v','l','s',' '),	HB_TAG('F','L','E',' ')},	/* Vlaams -> Dutch (Flemish) */
+  {HB_TAG('v','m','w',' '),	HB_TAG('M','A','K',' ')},	/* Makhuwa */
+/*{HB_TAG('v','r','o',' '),	HB_TAG('V','R','O',' ')},*/	/* Võro */
+  {HB_TAG('w','a','g',' '),	HB_TAG_NONE	       },	/* Wa'ema != Wagdi */
+/*{HB_TAG('w','a','r',' '),	HB_TAG('W','A','R',' ')},*/	/* Waray (Philippines) -> Waray-Waray */
+  {HB_TAG('w','b','m',' '),	HB_TAG('W','A',' ',' ')},	/* Wa */
+  {HB_TAG('w','b','r',' '),	HB_TAG('W','A','G',' ')},	/* Wagdi */
+  {HB_TAG('w','b','r',' '),	HB_TAG('R','A','J',' ')},	/* Wagdi -> Rajasthani */
+/*{HB_TAG('w','c','i',' '),	HB_TAG('W','C','I',' ')},*/	/* Waci Gbe */
+  {HB_TAG('w','e','a',' '),	HB_TAG('K','R','N',' ')},	/* Wewaw -> Karen */
+  {HB_TAG('w','e','s',' '),	HB_TAG('C','P','P',' ')},	/* Cameroon Pidgin -> Creoles */
+  {HB_TAG('w','e','u',' '),	HB_TAG('Q','I','N',' ')},	/* Rawngtu Chin -> Chin */
+  {HB_TAG('w','l','c',' '),	HB_TAG('C','M','R',' ')},	/* Mwali Comorian -> Comorian */
+  {HB_TAG('w','l','e',' '),	HB_TAG('S','I','G',' ')},	/* Wolane -> Silte Gurage */
+  {HB_TAG('w','l','k',' '),	HB_TAG('A','T','H',' ')},	/* Wailaki -> Athapaskan */
+  {HB_TAG('w','n','i',' '),	HB_TAG('C','M','R',' ')},	/* Ndzwani Comorian -> Comorian */
+  {HB_TAG('w','r','y',' '),	HB_TAG('M','A','W',' ')},	/* Merwari -> Marwari */
+  {HB_TAG('w','s','g',' '),	HB_TAG('G','O','N',' ')},	/* Adilabad Gondi -> Gondi */
+/*{HB_TAG('w','t','m',' '),	HB_TAG('W','T','M',' ')},*/	/* Mewati */
+  {HB_TAG('w','u','u',' '),	HB_TAG('Z','H','S',' ')},	/* Wu Chinese -> Chinese, Simplified */
+  {HB_TAG('x','a','l',' '),	HB_TAG('K','L','M',' ')},	/* Kalmyk */
+  {HB_TAG('x','a','l',' '),	HB_TAG('T','O','D',' ')},	/* Kalmyk -> Todo */
+  {HB_TAG('x','a','n',' '),	HB_TAG('S','E','K',' ')},	/* Xamtanga -> Sekota */
+  {HB_TAG('x','b','d',' '),	HB_TAG_NONE	       },	/* Bindal != Lü */
+/*{HB_TAG('x','j','b',' '),	HB_TAG('X','J','B',' ')},*/	/* Minjungbal -> Minjangbal */
+/*{HB_TAG('x','k','f',' '),	HB_TAG('X','K','F',' ')},*/	/* Khengkha */
+  {HB_TAG('x','m','g',' '),	HB_TAG('B','M','L',' ')},	/* Mengaka -> Bamileke */
+  {HB_TAG('x','m','m',' '),	HB_TAG('M','L','Y',' ')},	/* Manado Malay -> Malay */
+  {HB_TAG('x','m','m',' '),	HB_TAG('C','P','P',' ')},	/* Manado Malay -> Creoles */
+  {HB_TAG('x','m','v',' '),	HB_TAG('M','L','G',' ')},	/* Antankarana Malagasy -> Malagasy */
+  {HB_TAG('x','m','w',' '),	HB_TAG('M','L','G',' ')},	/* Tsimihety Malagasy -> Malagasy */
+  {HB_TAG('x','n','j',' '),	HB_TAG('S','X','T',' ')},	/* Ngoni (Tanzania) -> Sutu */
+  {HB_TAG('x','n','q',' '),	HB_TAG('S','X','T',' ')},	/* Ngoni (Mozambique) -> Sutu */
+  {HB_TAG('x','n','r',' '),	HB_TAG('D','G','R',' ')},	/* Kangri -> Dogri (macrolanguage) */
+/*{HB_TAG('x','o','g',' '),	HB_TAG('X','O','G',' ')},*/	/* Soga */
+  {HB_TAG('x','p','e',' '),	HB_TAG('X','P','E',' ')},	/* Liberia Kpelle -> Kpelle (Liberia) */
+  {HB_TAG('x','p','e',' '),	HB_TAG('K','P','L',' ')},	/* Liberia Kpelle -> Kpelle */
+  {HB_TAG('x','s','l',' '),	HB_TAG('S','S','L',' ')},	/* South Slavey */
+  {HB_TAG('x','s','l',' '),	HB_TAG('S','L','A',' ')},	/* South Slavey -> Slavey */
+  {HB_TAG('x','s','l',' '),	HB_TAG('A','T','H',' ')},	/* South Slavey -> Athapaskan */
+  {HB_TAG('x','s','t',' '),	HB_TAG('S','I','G',' ')},	/* Silt'e (retired code) -> Silte Gurage */
+/*{HB_TAG('x','u','b',' '),	HB_TAG('X','U','B',' ')},*/	/* Betta Kurumba -> Bette Kuruma */
+/*{HB_TAG('x','u','j',' '),	HB_TAG('X','U','J',' ')},*/	/* Jennu Kurumba -> Jennu Kuruma */
+  {HB_TAG('x','u','p',' '),	HB_TAG('A','T','H',' ')},	/* Upper Umpqua -> Athapaskan */
+  {HB_TAG('x','w','o',' '),	HB_TAG('T','O','D',' ')},	/* Written Oirat -> Todo */
+  {HB_TAG('y','a','j',' '),	HB_TAG('B','A','D','0')},	/* Banda-Yangere -> Banda */
+  {HB_TAG('y','a','k',' '),	HB_TAG_NONE	       },	/* Yakama != Sakha */
+/*{HB_TAG('y','a','o',' '),	HB_TAG('Y','A','O',' ')},*/	/* Yao */
+/*{HB_TAG('y','a','p',' '),	HB_TAG('Y','A','P',' ')},*/	/* Yapese */
+  {HB_TAG('y','b','a',' '),	HB_TAG_NONE	       },	/* Yala != Yoruba */
+  {HB_TAG('y','b','b',' '),	HB_TAG('B','M','L',' ')},	/* Yemba -> Bamileke */
+  {HB_TAG('y','b','d',' '),	HB_TAG('A','R','K',' ')},	/* Yangbye (retired code) -> Rakhine */
+  {HB_TAG('y','d','d',' '),	HB_TAG('J','I','I',' ')},	/* Eastern Yiddish -> Yiddish */
+/*{HB_TAG('y','g','p',' '),	HB_TAG('Y','G','P',' ')},*/	/* Gepo */
+  {HB_TAG('y','i','h',' '),	HB_TAG('J','I','I',' ')},	/* Western Yiddish -> Yiddish */
+  {HB_TAG('y','i','m',' '),	HB_TAG_NONE	       },	/* Yimchungru Naga != Yi Modern */
+/*{HB_TAG('y','n','a',' '),	HB_TAG('Y','N','A',' ')},*/	/* Aluo */
+  {HB_TAG('y','o','s',' '),	HB_TAG('Q','I','N',' ')},	/* Yos (retired code) -> Chin */
+  {HB_TAG('y','u','a',' '),	HB_TAG('M','Y','N',' ')},	/* Yucateco -> Mayan */
+  {HB_TAG('y','u','e',' '),	HB_TAG('Z','H','H',' ')},	/* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */
+/*{HB_TAG('y','w','q',' '),	HB_TAG('Y','W','Q',' ')},*/	/* Wuding-Luquan Yi */
+  {HB_TAG('z','c','h',' '),	HB_TAG('Z','H','A',' ')},	/* Central Hongshuihe Zhuang -> Zhuang */
+  {HB_TAG('z','d','j',' '),	HB_TAG('C','M','R',' ')},	/* Ngazidja Comorian -> Comorian */
+/*{HB_TAG('z','e','a',' '),	HB_TAG('Z','E','A',' ')},*/	/* Zeeuws -> Zealandic */
+  {HB_TAG('z','e','h',' '),	HB_TAG('Z','H','A',' ')},	/* Eastern Hongshuihe Zhuang -> Zhuang */
+  {HB_TAG('z','e','n',' '),	HB_TAG('B','B','R',' ')},	/* Zenaga -> Berber */
+  {HB_TAG('z','g','b',' '),	HB_TAG('Z','H','A',' ')},	/* Guibei Zhuang -> Zhuang */
+  {HB_TAG('z','g','h',' '),	HB_TAG('Z','G','H',' ')},	/* Standard Moroccan Tamazight */
+  {HB_TAG('z','g','h',' '),	HB_TAG('B','B','R',' ')},	/* Standard Moroccan Tamazight -> Berber */
+  {HB_TAG('z','g','m',' '),	HB_TAG('Z','H','A',' ')},	/* Minz Zhuang -> Zhuang */
+  {HB_TAG('z','g','n',' '),	HB_TAG('Z','H','A',' ')},	/* Guibian Zhuang -> Zhuang */
+  {HB_TAG('z','h','d',' '),	HB_TAG('Z','H','A',' ')},	/* Dai Zhuang -> Zhuang */
+  {HB_TAG('z','h','n',' '),	HB_TAG('Z','H','A',' ')},	/* Nong Zhuang -> Zhuang */
+  {HB_TAG('z','l','j',' '),	HB_TAG('Z','H','A',' ')},	/* Liujiang Zhuang -> Zhuang */
+  {HB_TAG('z','l','m',' '),	HB_TAG('M','L','Y',' ')},	/* Malay */
+  {HB_TAG('z','l','n',' '),	HB_TAG('Z','H','A',' ')},	/* Lianshan Zhuang -> Zhuang */
+  {HB_TAG('z','l','q',' '),	HB_TAG('Z','H','A',' ')},	/* Liuqian Zhuang -> Zhuang */
+  {HB_TAG('z','m','i',' '),	HB_TAG('M','L','Y',' ')},	/* Negeri Sembilan Malay -> Malay */
+  {HB_TAG('z','m','z',' '),	HB_TAG('B','A','D','0')},	/* Mbandja -> Banda */
+  {HB_TAG('z','n','d',' '),	HB_TAG_NONE	       },	/* Zande [collection] != Zande */
+  {HB_TAG('z','n','e',' '),	HB_TAG('Z','N','D',' ')},	/* Zande */
+  {HB_TAG('z','o','m',' '),	HB_TAG('Q','I','N',' ')},	/* Zou -> Chin */
+  {HB_TAG('z','q','e',' '),	HB_TAG('Z','H','A',' ')},	/* Qiubei Zhuang -> Zhuang */
+  {HB_TAG('z','s','m',' '),	HB_TAG('M','L','Y',' ')},	/* Standard Malay -> Malay */
+  {HB_TAG('z','u','m',' '),	HB_TAG('L','R','C',' ')},	/* Kumzari -> Luri */
+  {HB_TAG('z','y','b',' '),	HB_TAG('Z','H','A',' ')},	/* Yongbei Zhuang -> Zhuang */
+  {HB_TAG('z','y','g',' '),	HB_TAG('Z','H','A',' ')},	/* Yang Zhuang -> Zhuang */
+  {HB_TAG('z','y','j',' '),	HB_TAG('Z','H','A',' ')},	/* Youjiang Zhuang -> Zhuang */
+  {HB_TAG('z','y','n',' '),	HB_TAG('Z','H','A',' ')},	/* Yongnan Zhuang -> Zhuang */
+  {HB_TAG('z','y','p',' '),	HB_TAG('Q','I','N',' ')},	/* Zyphe Chin -> Chin */
+/*{HB_TAG('z','z','a',' '),	HB_TAG('Z','Z','A',' ')},*/	/* Zazaki [macrolanguage] */
+  {HB_TAG('z','z','j',' '),	HB_TAG('Z','H','A',' ')},	/* Zuojiang Zhuang -> Zhuang */
+};
+#endif
+
 /**
  * hb_ot_tags_from_complex_language:
  * @lang_str: a BCP 47 language tag to convert.
@@ -1633,75 +1638,81 @@ static const LangTag ot_languages[] = {
  *
  * Return value: Whether any language systems were retrieved.
  **/
-static bool
+static inline bool
 hb_ot_tags_from_complex_language (const char   *lang_str,
 				  const char   *limit,
 				  unsigned int *count /* IN/OUT */,
 				  hb_tag_t     *tags /* OUT */)
 {
-  if (subtag_matches (lang_str, limit, "-fonnapa"))
-  {
-    /* Undetermined; North American Phonetic Alphabet */
-    tags[0] = HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-polyton"))
-  {
-    /* Modern Greek (1453-); Polytonic Greek */
-    tags[0] = HB_TAG('P','G','R',' ');  /* Polytonic Greek */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-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 */
-    tags[0] = HB_TAG('P','R','O',' ');  /* Provençal / Old Provençal */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-fonipa"))
-  {
-    /* Undetermined; International Phonetic Alphabet */
-    tags[0] = HB_TAG('I','P','P','H');  /* Phonetic transcription—IPA conventions */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-geok"))
-  {
-    /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
-    tags[0] = HB_TAG('K','G','E',' ');  /* Khutsuri Georgian */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-syre"))
+  if (limit - lang_str >= 7)
   {
-    /* Undetermined; Syriac (Estrangelo variant) */
-    tags[0] = HB_TAG('S','Y','R','E');  /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-syrj"))
-  {
-    /* Undetermined; Syriac (Western variant) */
-    tags[0] = HB_TAG('S','Y','R','J');  /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-syrn"))
-  {
-    /* Undetermined; Syriac (Eastern variant) */
-    tags[0] = HB_TAG('S','Y','R','N');  /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */
-    *count = 1;
-    return true;
+    const char *p = strchr (lang_str, '-');
+    if (!p || p >= limit || limit - p < 5) goto out;
+    if (subtag_matches (p, limit, "-fonnapa", 8))
+    {
+      /* Undetermined; North American Phonetic Alphabet */
+      tags[0] = HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-polyton", 8))
+    {
+      /* Modern Greek (1453-); Polytonic Greek */
+      tags[0] = HB_TAG('P','G','R',' ');  /* Polytonic Greek */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-arevmda", 8))
+    {
+      /* Armenian; Western Armenian (retired code) */
+      tags[0] = HB_TAG('H','Y','E',' ');  /* Armenian */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-provenc", 8))
+    {
+      /* Occitan (post 1500); Provençal */
+      tags[0] = HB_TAG('P','R','O',' ');  /* Provençal / Old Provençal */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-fonipa", 7))
+    {
+      /* Undetermined; International Phonetic Alphabet */
+      tags[0] = HB_TAG('I','P','P','H');  /* Phonetic transcription—IPA conventions */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-geok", 5))
+    {
+      /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
+      tags[0] = HB_TAG('K','G','E',' ');  /* Khutsuri Georgian */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-syre", 5))
+    {
+      /* Undetermined; Syriac (Estrangelo variant) */
+      tags[0] = HB_TAG('S','Y','R','E');  /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-syrj", 5))
+    {
+      /* Undetermined; Syriac (Western variant) */
+      tags[0] = HB_TAG('S','Y','R','J');  /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-syrn", 5))
+    {
+      /* Undetermined; Syriac (Eastern variant) */
+      tags[0] = HB_TAG('S','Y','R','N');  /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */
+      *count = 1;
+      return true;
+    }
   }
+out:
   switch (lang_str[0])
   {
   case 'a':
@@ -1714,14 +1725,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'c':
-    if (lang_matches (&lang_str[1], "do-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "do-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "do-hant-mo", 10))
     {
       /* Min Dong Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1734,14 +1745,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "jy-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "jy-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "jy-hant-mo", 10))
     {
       /* Jinyu Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1754,14 +1765,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "mn-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "mn-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "mn-hant-mo", 10))
     {
       /* Mandarin Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1774,14 +1785,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10))
     {
       /* Northern Ping Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1794,14 +1805,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "px-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "px-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "px-hant-mo", 10))
     {
       /* Pu-Xian Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1814,14 +1825,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sp-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "sp-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "sp-hant-mo", 10))
     {
       /* Southern Ping Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1834,14 +1845,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zh-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "zh-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "zh-hant-mo", 10))
     {
       /* Huizhou Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1854,14 +1865,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zo-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "zo-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "zo-hant-mo", 10))
     {
       /* Min Zhong Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1874,112 +1885,112 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "do-hans"))
+    if (lang_matches (&lang_str[1], limit, "do-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "do-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "jy-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "jy-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "mn-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "mn-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "np-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "np-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "px-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "px-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "sp-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "sp-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "zh-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "zo-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "zo-hant", 7))
     {
       /* Min Zhong Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1987,7 +1998,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "do-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Dong Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1995,7 +2006,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "do-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Dong Chinese; Macao */
       unsigned int i;
@@ -2009,7 +2020,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "do-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Dong Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2017,7 +2028,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "jy-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Jinyu Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2025,7 +2036,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "jy-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Jinyu Chinese; Macao */
       unsigned int i;
@@ -2039,7 +2050,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "jy-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Jinyu Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2047,7 +2058,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "mn-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Mandarin Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2055,7 +2066,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "mn-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Mandarin Chinese; Macao */
       unsigned int i;
@@ -2069,7 +2080,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "mn-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Mandarin Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2077,7 +2088,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Northern Ping Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2085,7 +2096,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Northern Ping Chinese; Macao */
       unsigned int i;
@@ -2099,7 +2110,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Northern Ping Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2107,7 +2118,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "px-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Pu-Xian Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2115,7 +2126,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "px-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Pu-Xian Chinese; Macao */
       unsigned int i;
@@ -2129,7 +2140,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "px-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Pu-Xian Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2137,7 +2148,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sp-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Southern Ping Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2145,7 +2156,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sp-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Southern Ping Chinese; Macao */
       unsigned int i;
@@ -2159,7 +2170,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sp-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Southern Ping Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2167,7 +2178,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zh-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Huizhou Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2175,7 +2186,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zh-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Huizhou Chinese; Macao */
       unsigned int i;
@@ -2189,7 +2200,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zh-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Huizhou Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2197,7 +2208,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zo-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Zhong Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2205,7 +2216,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zo-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Zhong Chinese; Macao */
       unsigned int i;
@@ -2219,7 +2230,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zo-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Zhong Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2228,14 +2239,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'g':
-    if (lang_matches (&lang_str[1], "an-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10))
     {
       /* Gan Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2248,21 +2259,21 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hans"))
+    if (lang_matches (&lang_str[1], limit, "an-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "an-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "a-latg", 6))
     {
       /* Irish; Latin (Gaelic variant) */
       tags[0] = HB_TAG('I','R','T',' ');  /* Irish Traditional */
@@ -2270,7 +2281,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Gan Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2278,7 +2289,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Gan Chinese; Macao */
       unsigned int i;
@@ -2292,7 +2303,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Gan Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2301,14 +2312,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'h':
-    if (lang_matches (&lang_str[1], "ak-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "ak-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "ak-hant-mo", 10))
     {
       /* Hakka Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2321,14 +2332,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sn-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "sn-hant-mo", 10))
     {
       /* Xiang Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2341,28 +2352,28 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "ak-hans"))
+    if (lang_matches (&lang_str[1], limit, "ak-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "ak-hant", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "sn-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "sn-hant", 7))
     {
       /* Xiang Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2370,7 +2381,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "ak-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Hakka Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2378,7 +2389,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "ak-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Hakka Chinese; Macao */
       unsigned int i;
@@ -2392,7 +2403,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "ak-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Hakka Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2400,7 +2411,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Xiang Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2408,7 +2419,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Xiang Chinese; Macao */
       unsigned int i;
@@ -2422,7 +2433,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Xiang Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2460,7 +2471,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'l':
-    if (lang_matches (&lang_str[1], "zh-hans"))
+    if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
     {
       /* Literary Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2469,14 +2480,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'm':
-    if (lang_matches (&lang_str[1], "np-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10))
     {
       /* Min Bei Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2489,14 +2500,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hans"))
+    if (lang_matches (&lang_str[1], limit, "np-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "np-hant", 7))
     {
       /* Min Bei Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2504,7 +2515,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Bei Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2512,7 +2523,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Bei Chinese; Macao */
       unsigned int i;
@@ -2526,7 +2537,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Bei Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2534,7 +2545,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "nw-", 3)
-	&& subtag_matches (lang_str, limit, "-th"))
+	&& subtag_matches (lang_str, limit, "-th", 3))
     {
       /* Mon; Thailand */
       tags[0] = HB_TAG('M','O','N','T');  /* Thailand Mon */
@@ -2543,14 +2554,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'n':
-    if (lang_matches (&lang_str[1], "an-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10))
     {
       /* Min Nan Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2563,14 +2574,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hans"))
+    if (lang_matches (&lang_str[1], limit, "an-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "an-hant", 7))
     {
       /* Min Nan Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2578,7 +2589,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Nan Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2586,7 +2597,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Nan Chinese; Macao */
       unsigned int i;
@@ -2600,7 +2611,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Nan Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2624,7 +2635,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     break;
   case 'r':
     if (0 == strncmp (&lang_str[1], "o-", 2)
-	&& subtag_matches (lang_str, limit, "-md"))
+	&& subtag_matches (lang_str, limit, "-md", 3))
     {
       /* Romanian; Moldova */
       unsigned int i;
@@ -2639,14 +2650,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'w':
-    if (lang_matches (&lang_str[1], "uu-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "uu-hant-mo", 10))
     {
       /* Wu Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2659,14 +2670,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "uu-hans"))
+    if (lang_matches (&lang_str[1], limit, "uu-hans", 7))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "uu-hant", 7))
     {
       /* Wu Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2674,7 +2685,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "uu-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Wu Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2682,7 +2693,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "uu-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Wu Chinese; Macao */
       unsigned int i;
@@ -2696,7 +2707,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "uu-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Wu Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2705,7 +2716,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'y':
-    if (lang_matches (&lang_str[1], "ue-hans"))
+    if (lang_matches (&lang_str[1], limit, "ue-hans", 7))
     {
       /* Yue Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2714,14 +2725,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
     }
     break;
   case 'z':
-    if (lang_matches (&lang_str[1], "h-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "h-hant-hk", 9))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "h-hant-mo", 9))
     {
       /* Chinese [macrolanguage]; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2741,14 +2752,14 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "h-hans"))
+    if (lang_matches (&lang_str[1], limit, "h-hans", 6))
     {
       /* 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"))
+    if (lang_matches (&lang_str[1], limit, "h-hant", 6))
     {
       /* Chinese [macrolanguage]; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2763,7 +2774,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Chinese [macrolanguage]; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2771,7 +2782,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Chinese [macrolanguage]; Macao */
       unsigned int i;
@@ -2785,7 +2796,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Chinese [macrolanguage]; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2809,7 +2820,7 @@ hb_ot_tags_from_complex_language (const char   *lang_str,
  * Return value: The #hb_language_t corresponding to the BCP 47 language tag,
  * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.
  **/
-static hb_language_t
+static inline hb_language_t
 hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
 {
   switch (tag)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc
index f50be97ad3a3881bd6f938c87b1219c5c7640f96..ce5cdce98bd2e7ca872faefa8ae7b4f7fceac297 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc
@@ -189,48 +189,46 @@ hb_ot_tag_to_script (hb_tag_t tag)
 
 /* hb_language_t */
 
-static bool
+static inline bool
 subtag_matches (const char *lang_str,
 		const char *limit,
-		const char *subtag)
+		const char *subtag,
+		unsigned    subtag_len)
 {
+  if (likely ((unsigned) (limit - lang_str) < subtag_len))
+    return false;
+
   do {
     const char *s = strstr (lang_str, subtag);
     if (!s || s >= limit)
       return false;
-    if (!ISALNUM (s[strlen (subtag)]))
+    if (!ISALNUM (s[subtag_len]))
       return true;
-    lang_str = s + strlen (subtag);
+    lang_str = s + subtag_len;
   } while (true);
 }
 
-static hb_bool_t
-lang_matches (const char *lang_str, const char *spec)
+static bool
+lang_matches (const char *lang_str,
+	      const char *limit,
+	      const char *spec,
+	      unsigned    spec_len)
 {
-  unsigned int len = strlen (spec);
+  if (likely ((unsigned) (limit - lang_str) < spec_len))
+    return false;
 
-  return strncmp (lang_str, spec, len) == 0 &&
-	 (lang_str[len] == '\0' || lang_str[len] == '-');
+  return strncmp (lang_str, spec, spec_len) == 0 &&
+	 (lang_str[spec_len] == '\0' || lang_str[spec_len] == '-');
 }
 
 struct LangTag
 {
-  char language[4];
+  hb_tag_t language;
   hb_tag_t tag;
 
-  int cmp (const char *a) const
+  int cmp (hb_tag_t a) const
   {
-    const char *b = this->language;
-    unsigned int da, db;
-    const char *p;
-
-    p = strchr (a, '-');
-    da = p ? (unsigned int) (p - a) : strlen (a);
-
-    p = strchr (b, '-');
-    db = p ? (unsigned int) (p - b) : strlen (b);
-
-    return strncmp (a, b, hb_max (da, db));
+    return a < this->language ? -1 : a > this->language ? +1 : 0;
   }
   int cmp (const LangTag *that) const
   { return cmp (that->language); }
@@ -265,16 +263,19 @@ hb_ot_tags_from_language (const char   *lang_str,
 			  unsigned int *count,
 			  hb_tag_t     *tags)
 {
-  const char *s;
-  unsigned int tag_idx;
 
+#ifndef HB_NO_LANGUAGE_LONG
   /* Check for matches of multiple subtags. */
   if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags))
     return;
+#endif
 
   /* Find a language matching in the first component. */
-  s = strchr (lang_str, '-');
+#ifndef HB_NO_LANGUAGE_LONG
+  const char *s; s = strchr (lang_str, '-');
+#endif
   {
+#ifndef HB_NO_LANGUAGE_LONG
     if (s && limit - lang_str >= 6)
     {
       const char *extlang_end = strchr (s + 1, '-');
@@ -283,17 +284,42 @@ hb_ot_tags_from_language (const char   *lang_str,
 	  ISALPHA (s[1]))
 	lang_str = s + 1;
     }
-    if (hb_sorted_array (ot_languages).bfind (lang_str, &tag_idx))
+#endif
+    const LangTag *ot_languages = nullptr;
+    unsigned ot_languages_len = 0;
+    const char *dash = strchr (lang_str, '-');
+    unsigned first_len = dash ? dash - lang_str : limit - lang_str;
+    if (first_len == 2)
+    {
+      ot_languages = ot_languages2;
+      ot_languages_len = ARRAY_LENGTH (ot_languages2);
+    }
+#ifndef HB_NO_LANGUAGE_LONG
+    else if (first_len == 3)
     {
+      ot_languages = ot_languages3;
+      ot_languages_len = ARRAY_LENGTH (ot_languages3);
+    }
+#endif
+
+    hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len);
+
+    static hb_atomic_int_t last_tag_idx; /* Poor man's cache. */
+    unsigned tag_idx = last_tag_idx.get_relaxed ();
+
+    if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) ||
+	hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_tag, &tag_idx))
+    {
+      last_tag_idx.set_relaxed (tag_idx);
       unsigned int i;
       while (tag_idx != 0 &&
-	     0 == strcmp (ot_languages[tag_idx].language, ot_languages[tag_idx - 1].language))
+	     ot_languages[tag_idx].language == ot_languages[tag_idx - 1].language)
 	tag_idx--;
       for (i = 0;
 	   i < *count &&
-	   tag_idx + i < ARRAY_LENGTH (ot_languages) &&
+	   tag_idx + i < ot_languages_len &&
 	   ot_languages[tag_idx + i].tag != HB_TAG_NONE &&
-	   0 == strcmp (ot_languages[tag_idx + i].language, ot_languages[tag_idx].language);
+	   ot_languages[tag_idx + i].language == ot_languages[tag_idx].language;
 	   i++)
 	tags[i] = ot_languages[tag_idx + i].tag;
       *count = i;
@@ -301,6 +327,7 @@ hb_ot_tags_from_language (const char   *lang_str,
     }
   }
 
+#ifndef HB_NO_LANGUAGE_LONG
   if (!s)
     s = lang_str + strlen (lang_str);
   if (s - lang_str == 3) {
@@ -309,6 +336,7 @@ hb_ot_tags_from_language (const char   *lang_str,
     *count = 1;
     return;
   }
+#endif
 
   *count = 0;
 }
@@ -453,15 +481,29 @@ hb_ot_tag_to_language (hb_tag_t tag)
   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
     return nullptr;
 
+#ifndef HB_NO_LANGUAGE_LONG
   {
     hb_language_t disambiguated_tag = hb_ot_ambiguous_tag_to_language (tag);
     if (disambiguated_tag != HB_LANGUAGE_INVALID)
       return disambiguated_tag;
   }
+#endif
 
-  for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
-    if (ot_languages[i].tag == tag)
-      return hb_language_from_string (ot_languages[i].language, -1);
+  char buf[4];
+  for (i = 0; i < ARRAY_LENGTH (ot_languages2); i++)
+    if (ot_languages2[i].tag == tag)
+    {
+      hb_tag_to_string (ot_languages2[i].language, buf);
+      return hb_language_from_string (buf, 2);
+    }
+#ifndef HB_NO_LANGUAGE_LONG
+  for (i = 0; i < ARRAY_LENGTH (ot_languages3); i++)
+    if (ot_languages3[i].tag == tag)
+    {
+      hb_tag_to_string (ot_languages3[i].language, buf);
+      return hb_language_from_string (buf, 3);
+    }
+#endif
 
   /* Return a custom language in the form of "x-hbot-AABBCCDD".
    * If it's three letters long, also guess it's ISO 639-3 and lower-case and
@@ -557,16 +599,28 @@ hb_ot_tags_to_script_and_language (hb_tag_t       script_tag,
 static inline void
 test_langs_sorted ()
 {
-  for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++)
+  for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages2); i++)
   {
-    int c = ot_languages[i].cmp (&ot_languages[i - 1]);
+    int c = ot_languages2[i].cmp (&ot_languages2[i - 1]);
     if (c > 0)
     {
-      fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n",
-	       i, ot_languages[i-1].language, c, ot_languages[i].language);
+      fprintf (stderr, "ot_languages2 not sorted at index %d: %08x %d %08x\n",
+	       i, ot_languages2[i-1].language, c, ot_languages2[i].language);
       abort();
     }
   }
+#ifndef HB_NO_LANGUAGE_LONG
+  for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3); i++)
+  {
+    int c = ot_languages3[i].cmp (&ot_languages3[i - 1]);
+    if (c > 0)
+    {
+      fprintf (stderr, "ot_languages3 not sorted at index %d: %08x %d %08x\n",
+	       i, ot_languages3[i-1].language, c, ot_languages3[i].language);
+      abort();
+    }
+  }
+#endif
 }
 
 int
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-fvar-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-fvar-table.hh
index e066558683739ec746c1102ced41ff3226a5775e..c5c476bc0e06a36a3a960dbaf3b48f0a7db77364 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-fvar-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-fvar-table.hh
@@ -213,7 +213,7 @@ struct fvar
     if (!axis_index) axis_index = &i;
     *axis_index = HB_OT_VAR_NO_AXIS_INDEX;
     auto axes = get_axes ();
-    return axes.lfind (tag, axis_index) && (axes[*axis_index].get_axis_deprecated (info), true);
+    return axes.lfind (tag, axis_index) && ((void) axes[*axis_index].get_axis_deprecated (info), true);
   }
 #endif
   bool
@@ -221,7 +221,7 @@ struct fvar
   {
     unsigned i;
     auto axes = get_axes ();
-    return axes.lfind (tag, &i) && (axes[i].get_axis_info (i, info), true);
+    return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true);
   }
 
   int normalize_axis_value (unsigned int axis_index, float v) const
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh
index 618cec08fbf245162f04b961344aac9326272f0a..bf1039d1d6d484fd6016c370709ee1cbf9d5fabf 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh
@@ -45,9 +45,10 @@ struct contour_point_t
 
   void translate (const contour_point_t &p) { x += p.x; y += p.y; }
 
-  uint8_t flag;
-  float x, y;
-  bool is_end_point;
+  float x = 0.f;
+  float y = 0.f;
+  uint8_t flag = 0;
+  bool is_end_point = false;
 };
 
 struct contour_point_vector_t : hb_vector_t<contour_point_t>
@@ -55,16 +56,24 @@ struct contour_point_vector_t : hb_vector_t<contour_point_t>
   void extend (const hb_array_t<contour_point_t> &a)
   {
     unsigned int old_len = length;
-    resize (old_len + a.length);
-    for (unsigned int i = 0; i < a.length; i++)
-      (*this)[old_len + i] = a[i];
+    if (unlikely (!resize (old_len + a.length)))
+      return;
+    auto arrayZ = this->arrayZ + old_len;
+    unsigned count = a.length;
+    for (unsigned int i = 0; i < count; i++)
+      arrayZ[i] = a.arrayZ[i];
   }
 
   void transform (const float (&matrix)[4])
   {
-    for (unsigned int i = 0; i < length; i++)
+    if (matrix[0] == 1.f && matrix[1] == 0.f &&
+	matrix[2] == 0.f && matrix[3] == 1.f)
+      return;
+    auto arrayZ = this->arrayZ;
+    unsigned count = length;
+    for (unsigned i = 0; i < count; i++)
     {
-      contour_point_t &p = (*this)[i];
+      contour_point_t &p = arrayZ[i];
       float x_ = p.x * matrix[0] + p.y * matrix[2];
 	   p.y = p.x * matrix[1] + p.y * matrix[3];
       p.x = x_;
@@ -73,8 +82,12 @@ struct contour_point_vector_t : hb_vector_t<contour_point_t>
 
   void translate (const contour_point_t& delta)
   {
-    for (unsigned int i = 0; i < length; i++)
-      (*this)[i].translate (delta);
+    if (delta.x == 0.f && delta.y == 0.f)
+      return;
+    auto arrayZ = this->arrayZ;
+    unsigned count = length;
+    for (unsigned i = 0; i < count; i++)
+      arrayZ[i].translate (delta);
   }
 };
 
@@ -89,7 +102,7 @@ struct TupleVariationHeader
   const TupleVariationHeader &get_next (unsigned axis_count) const
   { return StructAtOffset<TupleVariationHeader> (this, get_size (axis_count)); }
 
-  float calculate_scalar (const int *coords, unsigned int coord_count,
+  float calculate_scalar (hb_array_t<int> coords, unsigned int coord_count,
 			  const hb_array_t<const F2DOT14> shared_tuples) const
   {
     hb_array_t<const F2DOT14> peak_tuple;
@@ -208,7 +221,7 @@ struct GlyphVariationData
       {
 	const HBUINT8 *base = &(var_data+var_data->data);
 	const HBUINT8 *p = base;
-	if (!unpack_points (p, shared_indices, var_data_bytes)) return false;
+	if (!unpack_points (p, shared_indices, (const HBUINT8 *) (var_data_bytes.arrayZ + var_data_bytes.length))) return false;
 	data_offset = p - base;
       }
       return true;
@@ -258,7 +271,7 @@ struct GlyphVariationData
 
   static bool unpack_points (const HBUINT8 *&p /* IN/OUT */,
 			     hb_vector_t<unsigned int> &points /* OUT */,
-			     const hb_bytes_t &bytes)
+			     const HBUINT8 *end)
   {
     enum packed_point_flag_t
     {
@@ -266,21 +279,21 @@ struct GlyphVariationData
       POINT_RUN_COUNT_MASK = 0x7F
     };
 
-    if (unlikely (!bytes.check_range (p))) return false;
+    if (unlikely (p + 1 > end)) return false;
 
     uint16_t count = *p++;
     if (count & POINTS_ARE_WORDS)
     {
-      if (unlikely (!bytes.check_range (p))) return false;
+      if (unlikely (p + 1 > end)) return false;
       count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++;
     }
-    points.resize (count);
+    if (unlikely (!points.resize (count))) return false;
 
     unsigned int n = 0;
     uint16_t i = 0;
     while (i < count)
     {
-      if (unlikely (!bytes.check_range (p))) return false;
+      if (unlikely (p + 1 > end)) return false;
       uint16_t j;
       uint8_t control = *p++;
       uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1;
@@ -288,8 +301,7 @@ struct GlyphVariationData
       {
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!bytes.check_range ((const HBUINT16 *) p)))
-	    return false;
+	  if (unlikely (p + HBUINT16::static_size > end)) return false;
 	  n += *(const HBUINT16 *)p;
 	  points[i] = n;
 	  p += HBUINT16::static_size;
@@ -299,7 +311,7 @@ struct GlyphVariationData
       {
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!bytes.check_range (p))) return false;
+	  if (unlikely (p + 1 > end)) return false;
 	  n += *p++;
 	  points[i] = n;
 	}
@@ -311,7 +323,7 @@ struct GlyphVariationData
 
   static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */,
 			     hb_vector_t<int> &deltas /* IN/OUT */,
-			     const hb_bytes_t &bytes)
+			     const HBUINT8 *end)
   {
     enum packed_delta_flag_t
     {
@@ -324,7 +336,7 @@ struct GlyphVariationData
     unsigned int count = deltas.length;
     while (i < count)
     {
-      if (unlikely (!bytes.check_range (p))) return false;
+      if (unlikely (p + 1 > end)) return false;
       uint8_t control = *p++;
       unsigned int run_count = (control & DELTA_RUN_COUNT_MASK) + 1;
       unsigned int j;
@@ -334,16 +346,14 @@ struct GlyphVariationData
       else if (control & DELTAS_ARE_WORDS)
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!bytes.check_range ((const HBUINT16 *) p)))
-	    return false;
+	  if (unlikely (p + HBUINT16::static_size > end)) return false;
 	  deltas[i] = *(const HBINT16 *) p;
 	  p += HBUINT16::static_size;
 	}
       else
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!bytes.check_range (p)))
-	    return false;
+	  if (unlikely (p + 1 > end)) return false;
 	  deltas[i] = *(const HBINT8 *) p++;
 	}
       if (j < run_count)
@@ -390,13 +400,10 @@ struct gvar
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && (version.major == 1) &&
-		  (glyphCount == c->get_num_glyphs ()) &&
 		  sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) &&
 		  (is_long_offset () ?
 		     c->check_array (get_long_offset_array (), glyphCount+1) :
-		     c->check_array (get_short_offset_array (), glyphCount+1)) &&
-		  c->check_array (((const HBUINT8*)&(this+dataZ)) + get_offset (0),
-				  get_offset (glyphCount) - get_offset (0)));
+		     c->check_array (get_short_offset_array (), glyphCount+1)));
   }
 
   /* GlyphVariationData not sanitized here; must be checked while accessing each glyph variation data */
@@ -482,7 +489,9 @@ struct gvar
   const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, hb_codepoint_t glyph) const
   {
     unsigned start_offset = get_offset (glyph);
-    unsigned length = get_offset (glyph+1) - start_offset;
+    unsigned end_offset = get_offset (glyph+1);
+    if (unlikely (end_offset < start_offset)) return hb_bytes_t ();
+    unsigned length = end_offset - start_offset;
     hb_bytes_t var_data = blob->as_bytes ().sub_array (((unsigned) dataZ) + start_offset, length);
     return likely (var_data.length >= GlyphVariationData::min_size) ? var_data : hb_bytes_t ();
   }
@@ -490,7 +499,10 @@ struct gvar
   bool is_long_offset () const { return flags & 1; }
 
   unsigned get_offset (unsigned i) const
-  { return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; }
+  {
+    if (unlikely (i > glyphCount)) return 0;
+    return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2;
+  }
 
   const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; }
   const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; }
@@ -503,19 +515,17 @@ struct gvar
     ~accelerator_t () { table.destroy (); }
 
     private:
-    struct x_getter { static float get (const contour_point_t &p) { return p.x; } };
-    struct y_getter { static float get (const contour_point_t &p) { return p.y; } };
 
-    template <typename T>
     static float infer_delta (const hb_array_t<contour_point_t> points,
 			      const hb_array_t<contour_point_t> deltas,
-			      unsigned int target, unsigned int prev, unsigned int next)
+			      unsigned int target, unsigned int prev, unsigned int next,
+			      float contour_point_t::*m)
     {
-      float target_val = T::get (points[target]);
-      float prev_val = T::get (points[prev]);
-      float next_val = T::get (points[next]);
-      float prev_delta = T::get (deltas[prev]);
-      float next_delta = T::get (deltas[next]);
+      float target_val = points[target].*m;
+      float prev_val = points[prev].*m;
+      float next_val = points[next].*m;
+      float prev_delta =  deltas[prev].*m;
+      float next_delta =  deltas[next].*m;
 
       if (prev_val == next_val)
 	return (prev_delta == next_delta) ? prev_delta : 0.f;
@@ -526,7 +536,7 @@ struct gvar
 
       /* linear interpolation */
       float r = (target_val - prev_val) / (next_val - prev_val);
-      return (1.f - r) * prev_delta + r * next_delta;
+      return prev_delta + r * (next_delta - prev_delta);
     }
 
     static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end)
@@ -536,8 +546,7 @@ struct gvar
     bool apply_deltas_to_points (hb_codepoint_t glyph, hb_font_t *font,
 				 const hb_array_t<contour_point_t> points) const
     {
-      /* num_coords should exactly match gvar's axisCount due to how GlyphVariationData tuples are aligned */
-      if (!font->num_coords || font->num_coords != table->axisCount) return true;
+      if (!font->num_coords) return true;
 
       if (unlikely (glyph >= table->glyphCount)) return true;
 
@@ -551,21 +560,25 @@ struct gvar
 
       /* Save original points for inferred delta calculation */
       contour_point_vector_t orig_points;
-      orig_points.resize (points.length);
+      if (unlikely (!orig_points.resize (points.length))) return false;
       for (unsigned int i = 0; i < orig_points.length; i++)
-	orig_points[i] = points[i];
+	orig_points.arrayZ[i] = points.arrayZ[i];
 
       contour_point_vector_t deltas; /* flag is used to indicate referenced point */
-      deltas.resize (points.length);
+      if (unlikely (!deltas.resize (points.length))) return false;
 
       hb_vector_t<unsigned> end_points;
       for (unsigned i = 0; i < points.length; ++i)
 	if (points[i].is_end_point)
 	  end_points.push (i);
 
-      int *coords = font->coords;
-      unsigned num_coords = font->num_coords;
+      auto coords = hb_array (font->coords, font->num_coords);
+      unsigned num_coords = table->axisCount;
       hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount);
+
+      hb_vector_t<unsigned int> private_indices;
+      hb_vector_t<int> x_deltas;
+      hb_vector_t<int> y_deltas;
       do
       {
 	float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples);
@@ -575,33 +588,30 @@ struct gvar
 	if (unlikely (!iterator.var_data_bytes.check_range (p, length)))
 	  return false;
 
-	hb_bytes_t bytes ((const char *) p, length);
-	hb_vector_t<unsigned int> private_indices;
+	const HBUINT8 *end = p + length;
+
 	bool has_private_points = iterator.current_tuple->has_private_points ();
 	if (has_private_points &&
-	    !GlyphVariationData::unpack_points (p, private_indices, bytes))
+	    !GlyphVariationData::unpack_points (p, private_indices, end))
 	  return false;
 	const hb_array_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices;
 
 	bool apply_to_all = (indices.length == 0);
 	unsigned int num_deltas = apply_to_all ? points.length : indices.length;
-	hb_vector_t<int> x_deltas;
-	x_deltas.resize (num_deltas);
-	if (!GlyphVariationData::unpack_deltas (p, x_deltas, bytes))
-	  return false;
-	hb_vector_t<int> y_deltas;
-	y_deltas.resize (num_deltas);
-	if (!GlyphVariationData::unpack_deltas (p, y_deltas, bytes))
-	  return false;
+	if (unlikely (!x_deltas.resize (num_deltas))) return false;
+	if (unlikely (!GlyphVariationData::unpack_deltas (p, x_deltas, end))) return false;
+	if (unlikely (!y_deltas.resize (num_deltas))) return false;
+	if (unlikely (!GlyphVariationData::unpack_deltas (p, y_deltas, end))) return false;
 
 	for (unsigned int i = 0; i < deltas.length; i++)
 	  deltas[i].init ();
 	for (unsigned int i = 0; i < num_deltas; i++)
 	{
 	  unsigned int pt_index = apply_to_all ? i : indices[i];
-	  deltas[pt_index].flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
-	  deltas[pt_index].x += x_deltas[i] * scalar;
-	  deltas[pt_index].y += y_deltas[i] * scalar;
+	  if (unlikely (pt_index >= deltas.length)) continue;
+	  deltas.arrayZ[pt_index].flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
+	  deltas.arrayZ[pt_index].x += x_deltas.arrayZ[i] * scalar;
+	  deltas.arrayZ[pt_index].y += y_deltas.arrayZ[i] * scalar;
 	}
 
 	/* infer deltas for unreferenced points */
@@ -645,20 +655,20 @@ struct gvar
 	    {
 	      i = next_index (i, start_point, end_point);
 	      if (i == next) break;
-	      deltas[i].x = infer_delta<x_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next);
-	      deltas[i].y = infer_delta<y_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next);
+	      deltas[i].x = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next, &contour_point_t::x);
+	      deltas[i].y = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next, &contour_point_t::y);
 	      if (--unref_count == 0) goto no_more_gaps;
 	    }
 	  }
-no_more_gaps:
+	no_more_gaps:
 	  start_point = end_point + 1;
 	}
 
 	/* apply specified / inferred deltas to points */
 	for (unsigned int i = 0; i < points.length; i++)
 	{
-	  points[i].x += deltas[i].x;
-	  points[i].y += deltas[i].y;
+	  points.arrayZ[i].x += deltas.arrayZ[i].x;
+	  points.arrayZ[i].y += deltas.arrayZ[i].y;
 	}
       } while (iterator.move_to_next ());
 
@@ -696,7 +706,7 @@ no_more_gaps:
 		offsetZ;	/* Offsets from the start of the GlyphVariationData array
 				 * to each GlyphVariationData table. */
   public:
-  DEFINE_SIZE_MIN (20);
+  DEFINE_SIZE_ARRAY (20, offsetZ);
 };
 
 struct gvar_accelerator_t : gvar::accelerator_t {
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-hvar-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-hvar-table.hh
index e9d90352f01f493728659c9d8e5601e612bbf55c..56efcdbee9e5dfcfac45fc7ac9762ade384b6078 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-hvar-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-hvar-table.hh
@@ -319,10 +319,15 @@ struct HVARVVAR
 						hvar_plan.index_map_plans.as_array ()));
   }
 
-  float get_advance_var (hb_codepoint_t glyph, hb_font_t *font) const
+  float get_advance_var (hb_codepoint_t  glyph,
+			 hb_font_t      *font,
+			 VariationStore::cache_t *store_cache = nullptr) const
   {
     uint32_t varidx = (this+advMap).map (glyph);
-    return (this+varStore).get_delta (varidx, font->coords, font->num_coords);
+    return (this+varStore).get_delta (varidx,
+				      font->coords,
+				      font->num_coords,
+				      store_cache);
   }
 
   float get_side_bearing_var (hb_codepoint_t glyph,
@@ -335,7 +340,7 @@ struct HVARVVAR
 
   bool has_side_bearing_deltas () const { return lsbMap && rsbMap; }
 
-  protected:
+  public:
   FixedVersion<>version;	/* Version of the metrics variation table
 				 * initially set to 0x00010000u */
   Offset32To<VariationStore>
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh
index 7d799ae9062b746a62194a0c6efa57283d571fc5..ffb86e30ae293d2f55630f03027ac7667b276505 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh
@@ -38,18 +38,11 @@
  */
 struct hb_priority_queue_t
 {
-  HB_DELETE_COPY_ASSIGN (hb_priority_queue_t);
-  hb_priority_queue_t ()  { init (); }
-  ~hb_priority_queue_t () { fini (); }
-
  private:
   typedef hb_pair_t<int64_t, unsigned> item_t;
   hb_vector_t<item_t> heap;
 
  public:
-  void init () { heap.init (); }
-
-  void fini () { heap.fini (); }
 
   void reset () { heap.resize (0); }
 
@@ -58,14 +51,17 @@ struct hb_priority_queue_t
   void insert (int64_t priority, unsigned value)
   {
     heap.push (item_t (priority, value));
+    if (unlikely (heap.in_error ())) return;
     bubble_up (heap.length - 1);
   }
 
   item_t pop_minimum ()
   {
-    item_t result = heap[0];
+    assert (!is_empty ());
+
+    item_t result = heap.arrayZ[0];
 
-    heap[0] = heap[heap.length - 1];
+    heap.arrayZ[0] = heap.arrayZ[heap.length - 1];
     heap.shrink (heap.length - 1);
     bubble_down (0);
 
@@ -104,6 +100,8 @@ struct hb_priority_queue_t
 
   void bubble_down (unsigned index)
   {
+    assert (index <= heap.length);
+
     unsigned left = left_child (index);
     unsigned right = right_child (index);
 
@@ -113,11 +111,11 @@ struct hb_priority_queue_t
       return;
 
     bool has_right = right < heap.length;
-    if (heap[index].first <= heap[left].first
-        && (!has_right || heap[index].first <= heap[right].first))
+    if (heap.arrayZ[index].first <= heap.arrayZ[left].first
+        && (!has_right || heap[index].first <= heap.arrayZ[right].first))
       return;
 
-    if (!has_right || heap[left].first < heap[right].first)
+    if (!has_right || heap.arrayZ[left].first < heap.arrayZ[right].first)
     {
       swap (index, left);
       bubble_down (left);
@@ -130,10 +128,12 @@ struct hb_priority_queue_t
 
   void bubble_up (unsigned index)
   {
+    assert (index <= heap.length);
+
     if (index == 0) return;
 
     unsigned parent_index = parent (index);
-    if (heap[parent_index].first <= heap[index].first)
+    if (heap.arrayZ[parent_index].first <= heap.arrayZ[index].first)
       return;
 
     swap (index, parent_index);
@@ -142,9 +142,9 @@ struct hb_priority_queue_t
 
   void swap (unsigned a, unsigned b)
   {
-    item_t temp = heap[a];
-    heap[a] = heap[b];
-    heap[b] = temp;
+    assert (a <= heap.length);
+    assert (b <= heap.length);
+    hb_swap (heap.arrayZ[a], heap.arrayZ[b]);
   }
 };
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh
index 2a9e75c45b1adbd8f6eb9b337a5cb6dbb5a5816c..683a441ec36f4bdf03f88993ee81c4e83a67681c 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh
@@ -32,1074 +32,18 @@
 #include "hb-priority-queue.hh"
 #include "hb-serialize.hh"
 #include "hb-vector.hh"
+#include "graph/graph.hh"
+#include "graph/serialize.hh"
+
+using graph::graph_t;
 
 /*
  * For a detailed writeup on the overflow resolution algorithm see:
  * docs/repacker.md
  */
-struct graph_t
-{
-  struct vertex_t
-  {
-    hb_serialize_context_t::object_t obj;
-    int64_t distance = 0 ;
-    int64_t space = 0 ;
-    hb_vector_t<unsigned> parents;
-    unsigned start = 0;
-    unsigned end = 0;
-    unsigned priority = 0;
-
-    bool is_shared () const
-    {
-      return parents.length > 1;
-    }
-
-    unsigned incoming_edges () const
-    {
-      return parents.length;
-    }
-
-    void remove_parent (unsigned parent_index)
-    {
-      for (unsigned i = 0; i < parents.length; i++)
-      {
-        if (parents[i] != parent_index) continue;
-        parents.remove (i);
-        break;
-      }
-    }
-
-    void remap_parents (const hb_vector_t<unsigned>& id_map)
-    {
-      for (unsigned i = 0; i < parents.length; i++)
-        parents[i] = id_map[parents[i]];
-    }
-
-    void remap_parent (unsigned old_index, unsigned new_index)
-    {
-      for (unsigned i = 0; i < parents.length; i++)
-      {
-        if (parents[i] == old_index)
-          parents[i] = new_index;
-      }
-    }
-
-    bool is_leaf () const
-    {
-      return !obj.real_links.length && !obj.virtual_links.length;
-    }
-
-    bool raise_priority ()
-    {
-      if (has_max_priority ()) return false;
-      priority++;
-      return true;
-    }
-
-    bool has_max_priority () const {
-      return priority >= 3;
-    }
-
-    int64_t modified_distance (unsigned order) const
-    {
-      // TODO(garretrieger): once priority is high enough, should try
-      // setting distance = 0 which will force to sort immediately after
-      // it's parent where possible.
-
-      int64_t modified_distance =
-          hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF);
-      if (has_max_priority ()) {
-        modified_distance = 0;
-      }
-      return (modified_distance << 18) | (0x003FFFF & order);
-    }
-
-    int64_t distance_modifier () const
-    {
-      if (!priority) return 0;
-      int64_t table_size = obj.tail - obj.head;
-
-      if (priority == 1)
-        return -table_size / 2;
-
-      return -table_size;
-    }
-  };
-
-  struct overflow_record_t
-  {
-    unsigned parent;
-    unsigned child;
-  };
-
-  /*
-   * A topological sorting of an object graph. Ordered
-   * in reverse serialization order (first object in the
-   * serialization is at the end of the list). This matches
-   * the 'packed' object stack used internally in the
-   * serializer
-   */
-  template<typename T>
-  graph_t (const T& objects)
-      : parents_invalid (true),
-        distance_invalid (true),
-        positions_invalid (true),
-        successful (true)
-  {
-    num_roots_for_space_.push (1);
-    bool removed_nil = false;
-    for (unsigned i = 0; i < objects.length; i++)
-    {
-      // TODO(grieger): check all links point to valid objects.
-
-      // If this graph came from a serialization buffer object 0 is the
-      // nil object. We don't need it for our purposes here so drop it.
-      if (i == 0 && !objects[i])
-      {
-        removed_nil = true;
-        continue;
-      }
-
-      vertex_t* v = vertices_.push ();
-      if (check_success (!vertices_.in_error ()))
-        v->obj = *objects[i];
-      if (!removed_nil) continue;
-      // Fix indices to account for removed nil object.
-      for (auto& l : v->obj.all_links_writer ()) {
-        l.objidx--;
-      }
-    }
-  }
-
-  ~graph_t ()
-  {
-    vertices_.fini ();
-  }
-
-  bool in_error () const
-  {
-    return !successful ||
-        vertices_.in_error () ||
-        num_roots_for_space_.in_error ();
-  }
-
-  const vertex_t& root () const
-  {
-    return vertices_[root_idx ()];
-  }
-
-  unsigned root_idx () const
-  {
-    // Object graphs are in reverse order, the first object is at the end
-    // of the vector. Since the graph is topologically sorted it's safe to
-    // assume the first object has no incoming edges.
-    return vertices_.length - 1;
-  }
-
-  const hb_serialize_context_t::object_t& object(unsigned i) const
-  {
-    return vertices_[i].obj;
-  }
-
-  /*
-   * serialize graph into the provided serialization buffer.
-   */
-  hb_blob_t* serialize () const
-  {
-    hb_vector_t<char> buffer;
-    size_t size = serialized_length ();
-    if (!buffer.alloc (size)) {
-      DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
-      return nullptr;
-    }
-    hb_serialize_context_t c((void *) buffer, size);
-
-    c.start_serialize<void> ();
-    for (unsigned i = 0; i < vertices_.length; i++) {
-      c.push ();
-
-      size_t size = vertices_[i].obj.tail - vertices_[i].obj.head;
-      char* start = c.allocate_size <char> (size);
-      if (!start) {
-        DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space.");
-        return nullptr;
-      }
-
-      memcpy (start, vertices_[i].obj.head, size);
-
-      // Only real links needs to be serialized.
-      for (const auto& link : vertices_[i].obj.real_links)
-        serialize_link (link, start, &c);
-
-      // All duplications are already encoded in the graph, so don't
-      // enable sharing during packing.
-      c.pop_pack (false);
-    }
-    c.end_serialize ();
-
-    if (c.in_error ()) {
-      DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d",
-                 c.errors);
-      return nullptr;
-    }
-
-    return c.copy_blob ();
-  }
-
-  /*
-   * Generates a new topological sorting of graph using Kahn's
-   * algorithm: https://en.wikipedia.org/wiki/Topological_sorting#Algorithms
-   */
-  void sort_kahn ()
-  {
-    positions_invalid = true;
-
-    if (vertices_.length <= 1) {
-      // Graph of 1 or less doesn't need sorting.
-      return;
-    }
-
-    hb_vector_t<unsigned> queue;
-    hb_vector_t<vertex_t> sorted_graph;
-    if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return;
-    hb_vector_t<unsigned> id_map;
-    if (unlikely (!check_success (id_map.resize (vertices_.length)))) return;
-
-    hb_vector_t<unsigned> removed_edges;
-    if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return;
-    update_parents ();
-
-    queue.push (root_idx ());
-    int new_id = vertices_.length - 1;
-
-    while (!queue.in_error () && queue.length)
-    {
-      unsigned next_id = queue[0];
-      queue.remove (0);
-
-      vertex_t& next = vertices_[next_id];
-      sorted_graph[new_id] = next;
-      id_map[next_id] = new_id--;
-
-      for (const auto& link : next.obj.all_links ()) {
-        removed_edges[link.objidx]++;
-        if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx]))
-          queue.push (link.objidx);
-      }
-    }
-
-    check_success (!queue.in_error ());
-    check_success (!sorted_graph.in_error ());
-    if (!check_success (new_id == -1))
-      print_orphaned_nodes ();
-
-    remap_all_obj_indices (id_map, &sorted_graph);
-
-    hb_swap (vertices_, sorted_graph);
-    sorted_graph.fini ();
-  }
-
-  /*
-   * Generates a new topological sorting of graph ordered by the shortest
-   * distance to each node.
-   */
-  void sort_shortest_distance ()
-  {
-    positions_invalid = true;
-
-    if (vertices_.length <= 1) {
-      // Graph of 1 or less doesn't need sorting.
-      return;
-    }
-
-    update_distances ();
-
-    hb_priority_queue_t queue;
-    hb_vector_t<vertex_t> sorted_graph;
-    if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return;
-    hb_vector_t<unsigned> id_map;
-    if (unlikely (!check_success (id_map.resize (vertices_.length)))) return;
-
-    hb_vector_t<unsigned> removed_edges;
-    if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return;
-    update_parents ();
-
-    queue.insert (root ().modified_distance (0), root_idx ());
-    int new_id = root_idx ();
-    unsigned order = 1;
-    while (!queue.in_error () && !queue.is_empty ())
-    {
-      unsigned next_id = queue.pop_minimum().second;
-
-      vertex_t& next = vertices_[next_id];
-      sorted_graph[new_id] = next;
-      id_map[next_id] = new_id--;
-
-      for (const auto& link : next.obj.all_links ()) {
-        removed_edges[link.objidx]++;
-        if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx]))
-          // Add the order that the links were encountered to the priority.
-          // This ensures that ties between priorities objects are broken in a consistent
-          // way. More specifically this is set up so that if a set of objects have the same
-          // distance they'll be added to the topological order in the order that they are
-          // referenced from the parent object.
-          queue.insert (vertices_[link.objidx].modified_distance (order++),
-                        link.objidx);
-      }
-    }
-
-    check_success (!queue.in_error ());
-    check_success (!sorted_graph.in_error ());
-    if (!check_success (new_id == -1))
-      print_orphaned_nodes ();
-
-    remap_all_obj_indices (id_map, &sorted_graph);
-
-    hb_swap (vertices_, sorted_graph);
-    sorted_graph.fini ();
-  }
-
-  /*
-   * Assign unique space numbers to each connected subgraph of 32 bit offset(s).
-   */
-  bool assign_32bit_spaces ()
-  {
-    unsigned root_index = root_idx ();
-    hb_set_t visited;
-    hb_set_t roots;
-    for (unsigned i = 0; i <= root_index; i++)
-    {
-      // Only real links can form 32 bit spaces
-      for (auto& l : vertices_[i].obj.real_links)
-      {
-        if (l.width == 4 && !l.is_signed)
-        {
-          roots.add (l.objidx);
-          find_subgraph (l.objidx, visited);
-        }
-      }
-    }
-
-    // Mark everything not in the subgraphs of 32 bit roots as visited.
-    // This prevents 32 bit subgraphs from being connected via nodes not in the 32 bit subgraphs.
-    visited.invert ();
-
-    if (!roots) return false;
-
-    while (roots)
-    {
-      unsigned next = HB_SET_VALUE_INVALID;
-      if (unlikely (!check_success (!roots.in_error ()))) break;
-      if (!roots.next (&next)) break;
-
-      hb_set_t connected_roots;
-      find_connected_nodes (next, roots, visited, connected_roots);
-      if (unlikely (!check_success (!connected_roots.in_error ()))) break;
-
-      isolate_subgraph (connected_roots);
-      if (unlikely (!check_success (!connected_roots.in_error ()))) break;
-
-      unsigned next_space = this->next_space ();
-      num_roots_for_space_.push (0);
-      for (unsigned root : connected_roots)
-      {
-        DEBUG_MSG (SUBSET_REPACK, nullptr, "Subgraph %u gets space %u", root, next_space);
-        vertices_[root].space = next_space;
-        num_roots_for_space_[next_space] = num_roots_for_space_[next_space] + 1;
-        distance_invalid = true;
-        positions_invalid = true;
-      }
-
-      // TODO(grieger): special case for GSUB/GPOS use extension promotions to move 16 bit space
-      //                into the 32 bit space as needed, instead of using isolation.
-    }
-
-
-
-    return true;
-  }
-
-  /*
-   * Isolates the subgraph of nodes reachable from root. Any links to nodes in the subgraph
-   * that originate from outside of the subgraph will be removed by duplicating the linked to
-   * object.
-   *
-   * Indices stored in roots will be updated if any of the roots are duplicated to new indices.
-   */
-  bool isolate_subgraph (hb_set_t& roots)
-  {
-    update_parents ();
-    hb_hashmap_t<unsigned, unsigned> subgraph;
-
-    // incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these
-    // set the subgraph incoming edge count to match all of root_idx's incoming edges
-    hb_set_t parents;
-    for (unsigned root_idx : roots)
-    {
-      subgraph.set (root_idx, wide_parents (root_idx, parents));
-      find_subgraph (root_idx, subgraph);
-    }
-
-    unsigned original_root_idx = root_idx ();
-    hb_hashmap_t<unsigned, unsigned> index_map;
-    bool made_changes = false;
-    for (auto entry : subgraph.iter ())
-    {
-      const auto& node = vertices_[entry.first];
-      unsigned subgraph_incoming_edges = entry.second;
-
-      if (subgraph_incoming_edges < node.incoming_edges ())
-      {
-        // Only  de-dup objects with incoming links from outside the subgraph.
-        made_changes = true;
-        duplicate_subgraph (entry.first, index_map);
-      }
-    }
-
-    if (!made_changes)
-      return false;
-
-    if (original_root_idx != root_idx ()
-        && parents.has (original_root_idx))
-    {
-      // If the root idx has changed since parents was determined, update root idx in parents
-      parents.add (root_idx ());
-      parents.del (original_root_idx);
-    }
-
-    auto new_subgraph =
-        + subgraph.keys ()
-        | hb_map([&] (unsigned node_idx) {
-          if (index_map.has (node_idx)) return index_map[node_idx];
-          return node_idx;
-        })
-        ;
-
-    remap_obj_indices (index_map, new_subgraph);
-    remap_obj_indices (index_map, parents.iter (), true);
-
-    // Update roots set with new indices as needed.
-    unsigned next = HB_SET_VALUE_INVALID;
-    while (roots.next (&next))
-    {
-      if (index_map.has (next))
-      {
-        roots.del (next);
-        roots.add (index_map[next]);
-      }
-    }
-
-    return true;
-  }
-
-  void find_subgraph (unsigned node_idx, hb_hashmap_t<unsigned, unsigned>& subgraph)
-  {
-    for (const auto& link : vertices_[node_idx].obj.all_links ())
-    {
-      if (subgraph.has (link.objidx))
-      {
-        subgraph.set (link.objidx, subgraph[link.objidx] + 1);
-        continue;
-      }
-      subgraph.set (link.objidx, 1);
-      find_subgraph (link.objidx, subgraph);
-    }
-  }
-
-  void find_subgraph (unsigned node_idx, hb_set_t& subgraph)
-  {
-    if (subgraph.has (node_idx)) return;
-    subgraph.add (node_idx);
-    for (const auto& link : vertices_[node_idx].obj.all_links ())
-      find_subgraph (link.objidx, subgraph);
-  }
-
-  /*
-   * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign
-   * links. index_map is updated with mappings from old id to new id. If a duplication has already
-   * been performed for a given index, then it will be skipped.
-   */
-  void duplicate_subgraph (unsigned node_idx, hb_hashmap_t<unsigned, unsigned>& index_map)
-  {
-    if (index_map.has (node_idx))
-      return;
-
-    index_map.set (node_idx, duplicate (node_idx));
-    for (const auto& l : object (node_idx).all_links ()) {
-      duplicate_subgraph (l.objidx, index_map);
-    }
-  }
-
-  /*
-   * Creates a copy of node_idx and returns it's new index.
-   */
-  unsigned duplicate (unsigned node_idx)
-  {
-    positions_invalid = true;
-    distance_invalid = true;
-
-    auto* clone = vertices_.push ();
-    auto& child = vertices_[node_idx];
-    if (vertices_.in_error ()) {
-      return -1;
-    }
-
-    clone->obj.head = child.obj.head;
-    clone->obj.tail = child.obj.tail;
-    clone->distance = child.distance;
-    clone->space = child.space;
-    clone->parents.reset ();
-
-    unsigned clone_idx = vertices_.length - 2;
-    for (const auto& l : child.obj.real_links)
-    {
-      clone->obj.real_links.push (l);
-      vertices_[l.objidx].parents.push (clone_idx);
-    }
-    for (const auto& l : child.obj.virtual_links)
-    {
-      clone->obj.virtual_links.push (l);
-      vertices_[l.objidx].parents.push (clone_idx);
-    }
-
-    check_success (!clone->obj.real_links.in_error ());
-    check_success (!clone->obj.virtual_links.in_error ());
-
-    // The last object is the root of the graph, so swap back the root to the end.
-    // The root's obj idx does change, however since it's root nothing else refers to it.
-    // all other obj idx's will be unaffected.
-    vertex_t root = vertices_[vertices_.length - 2];
-    vertices_[clone_idx] = *clone;
-    vertices_[vertices_.length - 1] = root;
-
-    // Since the root moved, update the parents arrays of all children on the root.
-    for (const auto& l : root.obj.all_links ())
-      vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ());
-
-    return clone_idx;
-  }
-
-  /*
-   * Creates a copy of child and re-assigns the link from
-   * parent to the clone. The copy is a shallow copy, objects
-   * linked from child are not duplicated.
-   */
-  bool duplicate (unsigned parent_idx, unsigned child_idx)
-  {
-    update_parents ();
-
-    unsigned links_to_child = 0;
-    for (const auto& l : vertices_[parent_idx].obj.all_links ())
-    {
-      if (l.objidx == child_idx) links_to_child++;
-    }
-
-    if (vertices_[child_idx].incoming_edges () <= links_to_child)
-    {
-      // Can't duplicate this node, doing so would orphan the original one as all remaining links
-      // to child are from parent.
-      DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %d => %d",
-                 parent_idx, child_idx);
-      return false;
-    }
-
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %d => %d",
-               parent_idx, child_idx);
-
-    unsigned clone_idx = duplicate (child_idx);
-    if (clone_idx == (unsigned) -1) return false;
-    // duplicate shifts the root node idx, so if parent_idx was root update it.
-    if (parent_idx == clone_idx) parent_idx++;
-
-    auto& parent = vertices_[parent_idx];
-    for (auto& l : parent.obj.all_links_writer ())
-    {
-      if (l.objidx != child_idx)
-        continue;
-
-      reassign_link (l, parent_idx, clone_idx);
-    }
-
-    return true;
-  }
-
-  /*
-   * Raises the sorting priority of all children.
-   */
-  bool raise_childrens_priority (unsigned parent_idx)
-  {
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Raising priority of all children of %d",
-               parent_idx);
-    // This operation doesn't change ordering until a sort is run, so no need
-    // to invalidate positions. It does not change graph structure so no need
-    // to update distances or edge counts.
-    auto& parent = vertices_[parent_idx].obj;
-    bool made_change = false;
-    for (auto& l : parent.all_links_writer ())
-      made_change |= vertices_[l.objidx].raise_priority ();
-    return made_change;
-  }
-
-  /*
-   * Will any offsets overflow on graph when it's serialized?
-   */
-  bool will_overflow (hb_vector_t<overflow_record_t>* overflows = nullptr)
-  {
-    if (overflows) overflows->resize (0);
-    update_positions ();
-
-    for (int parent_idx = vertices_.length - 1; parent_idx >= 0; parent_idx--)
-    {
-      // Don't need to check virtual links for overflow
-      for (const auto& link : vertices_[parent_idx].obj.real_links)
-      {
-        int64_t offset = compute_offset (parent_idx, link);
-        if (is_valid_offset (offset, link))
-          continue;
-
-        if (!overflows) return true;
-
-        overflow_record_t r;
-        r.parent = parent_idx;
-        r.child = link.objidx;
-        overflows->push (r);
-      }
-    }
-
-    if (!overflows) return false;
-    return overflows->length;
-  }
-
-  void print_orphaned_nodes ()
-  {
-    if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
-
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
-    parents_invalid = true;
-    update_parents();
-
-    for (unsigned i = 0; i < root_idx (); i++)
-    {
-      const auto& v = vertices_[i];
-      if (!v.parents)
-        DEBUG_MSG (SUBSET_REPACK, nullptr, "Node %u is orphaned.", i);
-    }
-  }
-
-  void print_overflows (const hb_vector_t<overflow_record_t>& overflows)
-  {
-    if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
-
-    update_parents ();
-    int limit = 10;
-    for (const auto& o : overflows)
-    {
-      if (!limit--) break;
-      const auto& parent = vertices_[o.parent];
-      const auto& child = vertices_[o.child];
-      DEBUG_MSG (SUBSET_REPACK, nullptr,
-                 "  overflow from "
-                 "%4d (%4d in, %4d out, space %2d) => "
-                 "%4d (%4d in, %4d out, space %2d)",
-                 o.parent,
-                 parent.incoming_edges (),
-                 parent.obj.real_links.length + parent.obj.virtual_links.length,
-                 space_for (o.parent),
-                 o.child,
-                 child.incoming_edges (),
-                 child.obj.real_links.length + child.obj.virtual_links.length,
-                 space_for (o.child));
-    }
-    if (overflows.length > 10) {
-      DEBUG_MSG (SUBSET_REPACK, nullptr, "  ... plus %d more overflows.", overflows.length - 10);
-    }
-  }
-
-  unsigned num_roots_for_space (unsigned space) const
-  {
-    return num_roots_for_space_[space];
-  }
-
-  unsigned next_space () const
-  {
-    return num_roots_for_space_.length;
-  }
-
-  void move_to_new_space (const hb_set_t& indices)
-  {
-    num_roots_for_space_.push (0);
-    unsigned new_space = num_roots_for_space_.length - 1;
-
-    for (unsigned index : indices) {
-      auto& node = vertices_[index];
-      num_roots_for_space_[node.space] = num_roots_for_space_[node.space] - 1;
-      num_roots_for_space_[new_space] = num_roots_for_space_[new_space] + 1;
-      node.space = new_space;
-      distance_invalid = true;
-      positions_invalid = true;
-    }
-  }
-
-  unsigned space_for (unsigned index, unsigned* root = nullptr) const
-  {
-    const auto& node = vertices_[index];
-    if (node.space)
-    {
-      if (root != nullptr)
-        *root = index;
-      return node.space;
-    }
-
-    if (!node.parents)
-    {
-      if (root)
-        *root = index;
-      return 0;
-    }
-
-    return space_for (node.parents[0], root);
-  }
-
-  void err_other_error () { this->successful = false; }
-
- private:
-
-  size_t serialized_length () const {
-    size_t total_size = 0;
-    for (unsigned i = 0; i < vertices_.length; i++) {
-      size_t size = vertices_[i].obj.tail - vertices_[i].obj.head;
-      total_size += size;
-    }
-    return total_size;
-  }
-
-  /*
-   * Returns the numbers of incoming edges that are 32bits wide.
-   */
-  unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const
-  {
-    unsigned count = 0;
-    hb_set_t visited;
-    for (unsigned p : vertices_[node_idx].parents)
-    {
-      if (visited.has (p)) continue;
-      visited.add (p);
-
-      // Only real links can be wide
-      for (const auto& l : vertices_[p].obj.real_links)
-      {
-        if (l.objidx == node_idx && l.width == 4 && !l.is_signed)
-        {
-          count++;
-          parents.add (p);
-        }
-      }
-    }
-    return count;
-  }
-
-  bool check_success (bool success)
-  { return this->successful && (success || (err_other_error (), false)); }
-
-  /*
-   * Creates a map from objid to # of incoming edges.
-   */
-  void update_parents ()
-  {
-    if (!parents_invalid) return;
-
-    for (unsigned i = 0; i < vertices_.length; i++)
-      vertices_[i].parents.reset ();
-
-    for (unsigned p = 0; p < vertices_.length; p++)
-    {
-      for (auto& l : vertices_[p].obj.all_links ())
-      {
-        vertices_[l.objidx].parents.push (p);
-      }
-    }
-
-    parents_invalid = false;
-  }
-
-  /*
-   * compute the serialized start and end positions for each vertex.
-   */
-  void update_positions ()
-  {
-    if (!positions_invalid) return;
-
-    unsigned current_pos = 0;
-    for (int i = root_idx (); i >= 0; i--)
-    {
-      auto& v = vertices_[i];
-      v.start = current_pos;
-      current_pos += v.obj.tail - v.obj.head;
-      v.end = current_pos;
-    }
-
-    positions_invalid = false;
-  }
-
-  /*
-   * Finds the distance to each object in the graph
-   * from the initial node.
-   */
-  void update_distances ()
-  {
-    if (!distance_invalid) return;
-
-    // Uses Dijkstra's algorithm to find all of the shortest distances.
-    // https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
-    //
-    // Implementation Note:
-    // Since our priority queue doesn't support fast priority decreases
-    // we instead just add new entries into the queue when a priority changes.
-    // Redundant ones are filtered out later on by the visited set.
-    // According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf
-    // for practical performance this is faster then using a more advanced queue
-    // (such as a fibonacci queue) with a fast decrease priority.
-    for (unsigned i = 0; i < vertices_.length; i++)
-    {
-      if (i == vertices_.length - 1)
-        vertices_[i].distance = 0;
-      else
-        vertices_[i].distance = hb_int_max (int64_t);
-    }
-
-    hb_priority_queue_t queue;
-    queue.insert (0, vertices_.length - 1);
-
-    hb_vector_t<bool> visited;
-    visited.resize (vertices_.length);
-
-    while (!queue.in_error () && !queue.is_empty ())
-    {
-      unsigned next_idx = queue.pop_minimum ().second;
-      if (visited[next_idx]) continue;
-      const auto& next = vertices_[next_idx];
-      int64_t next_distance = vertices_[next_idx].distance;
-      visited[next_idx] = true;
-
-      for (const auto& link : next.obj.all_links ())
-      {
-        if (visited[link.objidx]) continue;
-
-        const auto& child = vertices_[link.objidx].obj;
-        unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide
-        int64_t child_weight = (child.tail - child.head) +
-                               ((int64_t) 1 << (link_width * 8)) * (vertices_[link.objidx].space + 1);
-        int64_t child_distance = next_distance + child_weight;
-
-        if (child_distance < vertices_[link.objidx].distance)
-        {
-          vertices_[link.objidx].distance = child_distance;
-          queue.insert (child_distance, link.objidx);
-        }
-      }
-    }
-
-    check_success (!queue.in_error ());
-    if (!check_success (queue.is_empty ()))
-    {
-      print_orphaned_nodes ();
-      return;
-    }
-
-    distance_invalid = false;
-  }
-
-  int64_t compute_offset (
-      unsigned parent_idx,
-      const hb_serialize_context_t::object_t::link_t& link) const
-  {
-    const auto& parent = vertices_[parent_idx];
-    const auto& child = vertices_[link.objidx];
-    int64_t offset = 0;
-    switch ((hb_serialize_context_t::whence_t) link.whence) {
-      case hb_serialize_context_t::whence_t::Head:
-        offset = child.start - parent.start; break;
-      case hb_serialize_context_t::whence_t::Tail:
-        offset = child.start - parent.end; break;
-      case hb_serialize_context_t::whence_t::Absolute:
-        offset = child.start; break;
-    }
-
-    assert (offset >= link.bias);
-    offset -= link.bias;
-    return offset;
-  }
-
-  bool is_valid_offset (int64_t offset,
-                        const hb_serialize_context_t::object_t::link_t& link) const
-  {
-    if (unlikely (!link.width))
-      // Virtual links can't overflow.
-      return link.is_signed || offset >= 0;
-
-    if (link.is_signed)
-    {
-      if (link.width == 4)
-        return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
-      else
-        return offset >= -(1 << 15) && offset < (1 << 15);
-    }
-    else
-    {
-      if (link.width == 4)
-        return offset >= 0 && offset < ((int64_t) 1 << 32);
-      else if (link.width == 3)
-        return offset >= 0 && offset < ((int32_t) 1 << 24);
-      else
-        return offset >= 0 && offset < (1 << 16);
-    }
-  }
-
-  /*
-   * Updates a link in the graph to point to a different object. Corrects the
-   * parents vector on the previous and new child nodes.
-   */
-  void reassign_link (hb_serialize_context_t::object_t::link_t& link,
-                      unsigned parent_idx,
-                      unsigned new_idx)
-  {
-    unsigned old_idx = link.objidx;
-    link.objidx = new_idx;
-    vertices_[old_idx].remove_parent (parent_idx);
-    vertices_[new_idx].parents.push (parent_idx);
-  }
-
-  /*
-   * Updates all objidx's in all links using the provided mapping. Corrects incoming edge counts.
-   */
-  template<typename Iterator, hb_requires (hb_is_iterator (Iterator))>
-  void remap_obj_indices (const hb_hashmap_t<unsigned, unsigned>& id_map,
-                          Iterator subgraph,
-                          bool only_wide = false)
-  {
-    if (!id_map) return;
-    for (unsigned i : subgraph)
-    {
-      for (auto& link : vertices_[i].obj.all_links_writer ())
-      {
-        if (!id_map.has (link.objidx)) continue;
-        if (only_wide && !(link.width == 4 && !link.is_signed)) continue;
-
-        reassign_link (link, i, id_map[link.objidx]);
-      }
-    }
-  }
-
-  /*
-   * Updates all objidx's in all links using the provided mapping.
-   */
-  void remap_all_obj_indices (const hb_vector_t<unsigned>& id_map,
-                              hb_vector_t<vertex_t>* sorted_graph) const
-  {
-    for (unsigned i = 0; i < sorted_graph->length; i++)
-    {
-      (*sorted_graph)[i].remap_parents (id_map);
-      for (auto& link : (*sorted_graph)[i].obj.all_links_writer ())
-      {
-        link.objidx = id_map[link.objidx];
-      }
-    }
-  }
-
-  template <typename O> void
-  serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
-                          char* head,
-                          hb_serialize_context_t* c) const
-  {
-    OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
-    *offset = 0;
-    c->add_link (*offset,
-                 // serializer has an extra nil object at the start of the
-                 // object array. So all id's are +1 of what our id's are.
-                 link.objidx + 1,
-                 (hb_serialize_context_t::whence_t) link.whence,
-                 link.bias);
-  }
-
-  void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
-                 char* head,
-                 hb_serialize_context_t* c) const
-  {
-    switch (link.width)
-    {
-    case 0:
-      // Virtual links aren't serialized.
-      return;
-    case 4:
-      if (link.is_signed)
-      {
-        serialize_link_of_type<OT::HBINT32> (link, head, c);
-      } else {
-        serialize_link_of_type<OT::HBUINT32> (link, head, c);
-      }
-      return;
-    case 2:
-      if (link.is_signed)
-      {
-        serialize_link_of_type<OT::HBINT16> (link, head, c);
-      } else {
-        serialize_link_of_type<OT::HBUINT16> (link, head, c);
-      }
-      return;
-    case 3:
-      serialize_link_of_type<OT::HBUINT24> (link, head, c);
-      return;
-    default:
-      // Unexpected link width.
-      assert (0);
-    }
-  }
-
-  /*
-   * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped.
-   * For this search the graph is treated as being undirected.
-   *
-   * Connected targets will be added to connected and removed from targets. All visited nodes
-   * will be added to visited.
-   */
-  void find_connected_nodes (unsigned start_idx,
-                             hb_set_t& targets,
-                             hb_set_t& visited,
-                             hb_set_t& connected)
-  {
-    if (unlikely (!check_success (!visited.in_error ()))) return;
-    if (visited.has (start_idx)) return;
-    visited.add (start_idx);
-
-    if (targets.has (start_idx))
-    {
-      targets.del (start_idx);
-      connected.add (start_idx);
-    }
-
-    const auto& v = vertices_[start_idx];
-
-    // Graph is treated as undirected so search children and parents of start_idx
-    for (const auto& l : v.obj.all_links ())
-      find_connected_nodes (l.objidx, targets, visited, connected);
-
-    for (unsigned p : v.parents)
-      find_connected_nodes (p, targets, visited, connected);
-  }
-
- public:
-  // TODO(garretrieger): make private, will need to move most of offset overflow code into graph.
-  hb_vector_t<vertex_t> vertices_;
- private:
-  bool parents_invalid;
-  bool distance_invalid;
-  bool positions_invalid;
-  bool successful;
-  hb_vector_t<unsigned> num_roots_for_space_;
-};
 
 static inline
-bool _try_isolating_subgraphs (const hb_vector_t<graph_t::overflow_record_t>& overflows,
+bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& overflows,
                                graph_t& sorted_graph)
 {
   unsigned space = 0;
@@ -1107,7 +51,7 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph_t::overflow_record_t>& ov
 
   for (int i = overflows.length - 1; i >= 0; i--)
   {
-    const graph_t::overflow_record_t& r = overflows[i];
+    const graph::overflow_record_t& r = overflows[i];
 
     unsigned root;
     unsigned overflow_space = sorted_graph.space_for (r.parent, &root);
@@ -1149,7 +93,7 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph_t::overflow_record_t>& ov
 }
 
 static inline
-bool _process_overflows (const hb_vector_t<graph_t::overflow_record_t>& overflows,
+bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
                          hb_set_t& priority_bumped_parents,
                          graph_t& sorted_graph)
 {
@@ -1158,7 +102,7 @@ bool _process_overflows (const hb_vector_t<graph_t::overflow_record_t>& overflow
   // Try resolving the furthest overflows first.
   for (int i = overflows.length - 1; i >= 0; i--)
   {
-    const graph_t::overflow_record_t& r = overflows[i];
+    const graph::overflow_record_t& r = overflows[i];
     const auto& child = sorted_graph.vertices_[r.child];
     if (child.is_shared ())
     {
@@ -1214,20 +158,18 @@ inline hb_blob_t*
 hb_resolve_overflows (const T& packed,
                       hb_tag_t table_tag,
                       unsigned max_rounds = 20) {
-  // Kahn sort is ~twice as fast as shortest distance sort and works for many fonts
-  // so try it first to save time.
   graph_t sorted_graph (packed);
-  sorted_graph.sort_kahn ();
-  if (!sorted_graph.will_overflow ())
+  sorted_graph.sort_shortest_distance ();
+
+  bool will_overflow = graph::will_overflow (sorted_graph);
+  if (!will_overflow)
   {
-    return sorted_graph.serialize ();
+    return graph::serialize (sorted_graph);
   }
 
-  sorted_graph.sort_shortest_distance ();
-
   if ((table_tag == HB_OT_TAG_GPOS
        ||  table_tag == HB_OT_TAG_GSUB)
-      && sorted_graph.will_overflow ())
+      && will_overflow)
   {
     DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs.");
     if (sorted_graph.assign_32bit_spaces ())
@@ -1235,13 +177,13 @@ hb_resolve_overflows (const T& packed,
   }
 
   unsigned round = 0;
-  hb_vector_t<graph_t::overflow_record_t> overflows;
+  hb_vector_t<graph::overflow_record_t> overflows;
   // TODO(garretrieger): select a good limit for max rounds.
   while (!sorted_graph.in_error ()
-         && sorted_graph.will_overflow (&overflows)
+         && graph::will_overflow (sorted_graph, &overflows)
          && round++ < max_rounds) {
     DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Overflow resolution round %d ===", round);
-    sorted_graph.print_overflows (overflows);
+    print_overflows (sorted_graph, overflows);
 
     hb_set_t priority_bumped_parents;
 
@@ -1263,13 +205,13 @@ hb_resolve_overflows (const T& packed,
     return nullptr;
   }
 
-  if (sorted_graph.will_overflow ())
+  if (graph::will_overflow (sorted_graph))
   {
     DEBUG_MSG (SUBSET_REPACK, nullptr, "Offset overflow resolution failed.");
     return nullptr;
   }
 
-  return sorted_graph.serialize ();
+  return graph::serialize (sorted_graph);
 }
 
 #endif /* HB_REPACKER_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
index 40895a45482eaf5b661550f37fb17b4b56ac0ab6..cecdcdeb748ffcabdd272b59f952c2c23a45e134 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
@@ -74,7 +74,7 @@ struct hb_serialize_context_t
     }
 
     object_t () = default;
-    
+
 #ifdef HB_EXPERIMENTAL_API
     object_t (const hb_object_t &o)
     {
@@ -91,6 +91,15 @@ struct hb_serialize_context_t
     }
 #endif
 
+    friend void swap (object_t& a, object_t& b)
+    {
+      hb_swap (a.head, b.head);
+      hb_swap (a.tail, b.tail);
+      hb_swap (a.next, b.next);
+      hb_swap (a.real_links, b.real_links);
+      hb_swap (a.virtual_links, b.virtual_links);
+    }
+
     bool operator == (const object_t &o) const
     {
       // Virtual links aren't considered for equality since they don't affect the functionality
@@ -111,10 +120,10 @@ struct hb_serialize_context_t
     struct link_t
     {
       unsigned width: 3;
-      bool is_signed: 1;
+      unsigned is_signed: 1;
       unsigned whence: 2;
-      unsigned position: 28;
-      unsigned bias;
+      unsigned bias : 26;
+      unsigned position;
       objidx_t objidx;
 
       link_t () = default;
@@ -686,7 +695,7 @@ struct hb_serialize_context_t
     check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW);
   }
 
-  public: /* TODO Make private. */
+  public:
   char *start, *head, *tail, *end;
   unsigned int debug_depth;
   hb_serialize_error_t errors;
@@ -710,9 +719,7 @@ struct hb_serialize_context_t
   hb_vector_t<object_t *> packed;
 
   /* Map view of packed objects. */
-  hb_hashmap_t<const object_t *, objidx_t,
-	       const object_t *, objidx_t,
-	       nullptr, 0> packed_map;
+  hb_hashmap_t<const object_t *, objidx_t> packed_map;
 };
 
 #endif /* HB_SERIALIZE_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-set-digest.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-set-digest.hh
index 7d4979b73bd5f619828eae7b348d2c85dfcbf323..fab36216e40b3ed5ccd87ea07f3a7d987aa10cf8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-set-digest.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-set-digest.hh
@@ -30,7 +30,7 @@
 #include "hb.hh"
 
 /*
- * The set digests here implement various "filters" that support
+ * The set-digests here implement various "filters" that support
  * "approximate member query".  Conceptually these are like Bloom
  * Filter and Quotient Filter, however, much smaller, faster, and
  * designed to fit the requirements of our uses for glyph coverage
@@ -40,13 +40,25 @@
  * set of glyphs, but fully flooded and ineffective if coverage is
  * all over the place.
  *
- * The frozen-set can be used instead of a digest, to trade more
- * memory for 100% accuracy, but in practice, that doesn't look like
- * an attractive trade-off.
+ * The way these are used is that the filter is first populated by
+ * a lookup's or subtable's Coverage table(s), and then when we
+ * want to apply the lookup or subtable to a glyph, before trying
+ * to apply, we ask the filter if the glyph may be covered. If it's
+ * not, we return early.
+ *
+ * We use these filters both at the lookup-level, and then again,
+ * at the subtable-level. Both have performance win.
+ *
+ * The main filter we use is a combination of three bits-pattern
+ * filters. A bits-pattern filter checks a number of bits (5 or 6)
+ * of the input number (glyph-id in this case) and checks whether
+ * its pattern is amongst the patterns of any of the accepted values.
+ * The accepted patterns are represented as a "long" integer. The
+ * check is done using four bitwise operations only.
  */
 
 template <typename mask_t, unsigned int shift>
-struct hb_set_digest_lowest_bits_t
+struct hb_set_digest_bits_pattern_t
 {
   static constexpr unsigned mask_bytes = sizeof (mask_t);
   static constexpr unsigned mask_bits = sizeof (mask_t) * 8;
@@ -102,7 +114,7 @@ struct hb_set_digest_lowest_bits_t
   bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
 
   bool may_have (hb_codepoint_t g) const
-  { return !!(mask & mask_for (g)); }
+  { return mask & mask_for (g); }
 
   private:
 
@@ -171,11 +183,11 @@ struct hb_set_digest_combiner_t
 using hb_set_digest_t =
   hb_set_digest_combiner_t
   <
-    hb_set_digest_lowest_bits_t<unsigned long, 4>,
+    hb_set_digest_bits_pattern_t<unsigned long, 4>,
     hb_set_digest_combiner_t
     <
-      hb_set_digest_lowest_bits_t<unsigned long, 0>,
-      hb_set_digest_lowest_bits_t<unsigned long, 9>
+      hb_set_digest_bits_pattern_t<unsigned long, 0>,
+      hb_set_digest_bits_pattern_t<unsigned long, 9>
     >
   >
 ;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.cc
index 0e2c1f77efe5d27043970c004710be4fad6e2bdd..2d458294f8adcdf6ef333c3f2889b15ea5ec1de8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.cc
@@ -40,7 +40,7 @@
 
 
 /**
- * hb_set_create: (Xconstructor)
+ * hb_set_create:
  *
  * Creates a new, initially empty set.
  *
@@ -186,6 +186,7 @@ hb_set_t *
 hb_set_copy (const hb_set_t *set)
 {
   hb_set_t *copy = hb_set_create ();
+  if (unlikely (!copy)) return nullptr;
   copy->set (*set);
   return copy;
 }
@@ -358,6 +359,23 @@ hb_set_is_equal (const hb_set_t *set,
   return set->is_equal (*other);
 }
 
+/**
+ * hb_set_hash:
+ * @set: A set
+ *
+ * Creates a hash representing @set.
+ *
+ * Return value:
+ * A hash of @set.
+ *
+ * Since: 4.4.0
+ **/
+HB_EXTERN unsigned int
+hb_set_hash (const hb_set_t *set)
+{
+  return set->hash ();
+}
+
 /**
  * hb_set_is_subset:
  * @set: A set
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.h
index 10ce7c10d4167a843239f7fbd20b0b100d530775..56902c267ec322a1a365076c63d1246b2f9abe98 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.h
@@ -128,6 +128,9 @@ HB_EXTERN hb_bool_t
 hb_set_is_equal (const hb_set_t *set,
 		 const hb_set_t *other);
 
+HB_EXTERN unsigned int
+hb_set_hash (const hb_set_t *set);
+
 HB_EXTERN hb_bool_t
 hb_set_is_subset (const hb_set_t *set,
 		  const hb_set_t *larger_set);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
index 1f054078692a9bfbfe3fbb744ba829d07dda1224..7eb5e19a2a634c0d3291f53ea0f5e4e8ef00b2d2 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
@@ -43,8 +43,8 @@ struct hb_sparseset_t
 
   hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); }
   hb_sparseset_t (hb_sparseset_t&& other) : hb_sparseset_t () { s = std::move (other.s); }
-  hb_sparseset_t& operator= (const hb_sparseset_t& other) { set (other); return *this; }
-  hb_sparseset_t& operator= (hb_sparseset_t&& other) { hb_swap (*this, other); return *this; }
+  hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; }
+  hb_sparseset_t& operator = (hb_sparseset_t&& other) { s = std::move (other.s); return *this; }
   friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) { hb_swap (a.s, b.s); }
 
   hb_sparseset_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t ()
@@ -53,7 +53,7 @@ struct hb_sparseset_t
       add (item);
   }
   template <typename Iterable,
-	    hb_requires (hb_is_iterable (Iterable))>
+           hb_requires (hb_is_iterable (Iterable))>
   hb_sparseset_t (const Iterable &o) : hb_sparseset_t ()
   {
     hb_copy (o, *this);
@@ -77,10 +77,12 @@ struct hb_sparseset_t
   void err () { s.err (); }
   bool in_error () const { return s.in_error (); }
 
+  void alloc (unsigned sz) { s.alloc (sz); }
   void reset () { s.reset (); }
   void clear () { s.clear (); }
   void invert () { s.invert (); }
   bool is_empty () const { return s.is_empty (); }
+  uint32_t hash () const { return s.hash (); }
 
   void add (hb_codepoint_t g) { s.add (g); }
   bool add_range (hb_codepoint_t a, hb_codepoint_t b) { return s.add_range (a, b); }
@@ -125,6 +127,8 @@ struct hb_sparseset_t
   void set (const hb_sparseset_t &other) { s.set (other.s); }
 
   bool is_equal (const hb_sparseset_t &other) const { return s.is_equal (other.s); }
+  bool operator == (const hb_set_t &other) const { return is_equal (other); }
+  bool operator != (const hb_set_t &other) const { return !is_equal (other); }
 
   bool is_subset (const hb_sparseset_t &larger_set) const { return s.is_subset (larger_set.s); }
 
@@ -158,15 +162,18 @@ struct hb_sparseset_t
 
 struct hb_set_t : hb_sparseset_t<hb_bit_set_invertible_t>
 {
-  hb_set_t () = default;
+  using sparseset = hb_sparseset_t<hb_bit_set_invertible_t>;
+
   ~hb_set_t () = default;
-  hb_set_t (hb_set_t&) = default;
-  hb_set_t& operator= (const hb_set_t&) = default;
-  hb_set_t& operator= (hb_set_t&&) = default;
-  hb_set_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t<hb_bit_set_invertible_t> (lst) {}
+  hb_set_t () : sparseset () {};
+  hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {};
+  hb_set_t (hb_set_t&& o) : sparseset (std::move ((sparseset &) o)) {}
+  hb_set_t& operator = (const hb_set_t&) = default;
+  hb_set_t& operator = (hb_set_t&&) = default;
+  hb_set_t (std::initializer_list<hb_codepoint_t> lst) : sparseset (lst) {}
   template <typename Iterable,
 	    hb_requires (hb_is_iterable (Iterable))>
-  hb_set_t (const Iterable &o) : hb_sparseset_t<hb_bit_set_invertible_t> (o) {}
+  hb_set_t (const Iterable &o) : sparseset (o) {}
 };
 
 static_assert (hb_set_t::INVALID == HB_SET_VALUE_INVALID, "");
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-shape-plan.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-shape-plan.cc
index 66332165c37c2c6a5a6095661731de611a6bd38f..0af07825fc956bc7f596e771dfe9d553003ba4ae 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-shape-plan.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-shape-plan.cc
@@ -170,7 +170,7 @@ hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other)
 
 
 /**
- * hb_shape_plan_create: (Xconstructor)
+ * hb_shape_plan_create:
  * @face: #hb_face_t to use
  * @props: The #hb_segment_properties_t of the segment
  * @user_features: (array length=num_user_features): The list of user-selected features
@@ -198,7 +198,7 @@ hb_shape_plan_create (hb_face_t                     *face,
 }
 
 /**
- * hb_shape_plan_create2: (Xconstructor)
+ * hb_shape_plan_create2:
  * @face: #hb_face_t to use
  * @props: The #hb_segment_properties_t of the segment
  * @user_features: (array length=num_user_features): The list of user-selected features
@@ -231,7 +231,8 @@ hb_shape_plan_create2 (hb_face_t                     *face,
 		  num_coords,
 		  shaper_list);
 
-  assert (props->direction != HB_DIRECTION_INVALID);
+  if (unlikely (props->direction == HB_DIRECTION_INVALID))
+    return hb_shape_plan_get_empty ();
 
   hb_shape_plan_t *shape_plan;
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-shape.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-shape.cc
index 3407e1af42d2d5a1a8d5131533c89ab35435c4c0..14ec92828fcd446ddca695bc5df92c65ffb07cd1 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-shape.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-shape.cc
@@ -50,7 +50,7 @@
 
 static inline void free_static_shaper_list ();
 
-static const char *nil_shaper_list[] = {nullptr};
+static const char * const nil_shaper_list[] = {nullptr};
 
 static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *,
 							      hb_shaper_list_lazy_loader_t>
@@ -73,7 +73,7 @@ static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *,
   }
   static void destroy (const char **l)
   { hb_free (l); }
-  static const char ** get_null ()
+  static const char * const * get_null ()
   { return nil_shaper_list; }
 } static_shaper_list;
 
@@ -126,6 +126,11 @@ hb_shape_full (hb_font_t          *font,
 	       unsigned int        num_features,
 	       const char * const *shaper_list)
 {
+  if (unlikely (!buffer->len))
+    return true;
+
+  buffer->enter ();
+
   hb_buffer_t *text_buffer = nullptr;
   if (buffer->flags & HB_BUFFER_FLAG_VERIFY)
   {
@@ -137,12 +142,19 @@ hb_shape_full (hb_font_t          *font,
 							      features, num_features,
 							      font->coords, font->num_coords,
 							      shaper_list);
+
   hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
+
+  if (buffer->max_ops <= 0)
+    buffer->shaping_failed = true;
+
   hb_shape_plan_destroy (shape_plan);
 
   if (text_buffer)
   {
-    if (res && !buffer->verify (text_buffer,
+    if (res && buffer->successful && !buffer->shaping_failed
+	    && text_buffer->successful
+	    && !buffer->verify (text_buffer,
 				font,
 				features,
 				num_features,
@@ -151,6 +163,8 @@ hb_shape_full (hb_font_t          *font,
     hb_buffer_destroy (text_buffer);
   }
 
+  buffer->leave ();
+
   return res;
 }
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-shaper.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-shaper.cc
index a11ed83afd3559bd1ef8ee652df221813abcea07..da4253ed649468c9af691dd0bc5bf9d457a48674 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-shaper.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-shaper.cc
@@ -29,18 +29,18 @@
 #include "hb-machinery.hh"
 
 
-static const hb_shaper_entry_t all_shapers[] = {
+static const hb_shaper_entry_t _hb_all_shapers[] = {
 #define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape},
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 };
 #ifndef HB_NO_SHAPER
-static_assert (0 != ARRAY_LENGTH_CONST (all_shapers), "No shaper enabled.");
+static_assert (0 != ARRAY_LENGTH_CONST (_hb_all_shapers), "No shaper enabled.");
 #endif
 
 static inline void free_static_shapers ();
 
-static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_entry_t,
+static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<hb_shaper_entry_t,
 							  hb_shapers_lazy_loader_t>
 {
   static hb_shaper_entry_t *create ()
@@ -49,11 +49,11 @@ static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_entry_
     if (!env || !*env)
       return nullptr;
 
-    hb_shaper_entry_t *shapers = (hb_shaper_entry_t *) hb_calloc (1, sizeof (all_shapers));
+    hb_shaper_entry_t *shapers = (hb_shaper_entry_t *) hb_calloc (1, sizeof (_hb_all_shapers));
     if (unlikely (!shapers))
       return nullptr;
 
-    memcpy (shapers, all_shapers, sizeof (all_shapers));
+    memcpy (shapers, _hb_all_shapers, sizeof (_hb_all_shapers));
 
      /* Reorder shaper list to prefer requested shapers. */
     unsigned int i = 0;
@@ -64,7 +64,7 @@ static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_entry_
       if (!end)
 	end = p + strlen (p);
 
-      for (unsigned int j = i; j < ARRAY_LENGTH (all_shapers); j++)
+      for (unsigned int j = i; j < ARRAY_LENGTH (_hb_all_shapers); j++)
 	if (end - p == (int) strlen (shapers[j].name) &&
 	    0 == strncmp (shapers[j].name, p, end - p))
 	{
@@ -85,8 +85,8 @@ static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_entry_
 
     return shapers;
   }
-  static void destroy (const hb_shaper_entry_t *p) { hb_free ((void *) p); }
-  static const hb_shaper_entry_t *get_null ()      { return all_shapers; }
+  static void destroy (hb_shaper_entry_t *p) { hb_free (p); }
+  static const hb_shaper_entry_t *get_null ()      { return _hb_all_shapers; }
 } static_shapers;
 
 static inline
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-static.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-static.cc
index 7cc51be611c8e5fca7954eb20afd655fef2ea4e7..5c5ecce880949edc114c12390da4b668e9dcd725 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-static.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-static.cc
@@ -53,6 +53,9 @@ DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF};
 const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF};
 
 
+/* hb_map_t */
+
+const hb_codepoint_t minus_1 = -1;
 
 /* hb_face_t */
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh
index 18657705fac3d9376e108dd4d89c9366016a804b..bb9f27eec12058dbefa91fcbd8d91aed04176e79 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh
@@ -40,11 +40,12 @@ struct str_encoder_t
   str_encoder_t (str_buff_t &buff_)
     : buff (buff_), error (false) {}
 
-  void reset () { buff.resize (0); }
+  void reset () { buff.reset (); }
 
   void encode_byte (unsigned char b)
   {
-    if (unlikely (buff.push (b) == &Crap (unsigned char)))
+    buff.push (b);
+    if (unlikely (buff.in_error ()))
       set_error ();
   }
 
@@ -107,20 +108,18 @@ struct str_encoder_t
       encode_byte (op);
   }
 
-  void copy_str (const byte_str_t &str)
+  void copy_str (const hb_ubytes_t &str)
   {
     unsigned int  offset = buff.length;
-    if (unlikely (!buff.resize (offset + str.length)))
+    /* Manually resize buffer since faster. */
+    if ((signed) (buff.length + str.length) <= buff.allocated)
+      buff.length += str.length;
+    else if (unlikely (!buff.resize (offset + str.length)))
     {
       set_error ();
       return;
     }
-    if (unlikely (buff.length < offset + str.length))
-    {
-      set_error ();
-      return;
-    }
-    memcpy (&buff[offset], &str[0], str.length);
+    memcpy (buff.arrayZ + offset, &str[0], str.length);
   }
 
   bool is_error () const { return error; }
@@ -253,12 +252,12 @@ struct subr_flattener_t
 	if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op);
 	continue;
       }
-      const byte_str_t str = (*acc.charStrings)[glyph];
+      const hb_ubytes_t str = (*acc.charStrings)[glyph];
       unsigned int fd = acc.fdSelect->get_fd (glyph);
       if (unlikely (fd >= acc.fdCount))
 	return false;
-      cs_interpreter_t<ENV, OPSET, flatten_param_t> interp;
-      interp.env.init (str, acc, fd);
+      ENV env (str, acc, fd);
+      cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env);
       flatten_param_t  param = {
         flat_charstrings[i],
         (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
@@ -317,9 +316,9 @@ struct parsed_cs_op_t : op_str_t
   unsigned int  subr_num;
 
   protected:
-  bool	  drop_flag : 1;
-  bool	  keep_flag : 1;
-  bool	  skip_flag : 1;
+  bool	  drop_flag;
+  bool	  keep_flag;
+  bool	  skip_flag;
 };
 
 struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t>
@@ -398,19 +397,19 @@ struct parsed_cs_str_vec_t : hb_vector_t<parsed_cs_str_t>
 
 struct subr_subset_param_t
 {
-  void init (parsed_cs_str_t *parsed_charstring_,
-	     parsed_cs_str_vec_t *parsed_global_subrs_, parsed_cs_str_vec_t *parsed_local_subrs_,
-	     hb_set_t *global_closure_, hb_set_t *local_closure_,
-	     bool drop_hints_)
-  {
-    parsed_charstring = parsed_charstring_;
-    current_parsed_str = parsed_charstring;
-    parsed_global_subrs = parsed_global_subrs_;
-    parsed_local_subrs = parsed_local_subrs_;
-    global_closure = global_closure_;
-    local_closure = local_closure_;
-    drop_hints = drop_hints_;
-  }
+  subr_subset_param_t (parsed_cs_str_t *parsed_charstring_,
+		       parsed_cs_str_vec_t *parsed_global_subrs_,
+		       parsed_cs_str_vec_t *parsed_local_subrs_,
+		       hb_set_t *global_closure_,
+		       hb_set_t *local_closure_,
+		       bool drop_hints_) :
+      current_parsed_str (parsed_charstring_),
+      parsed_charstring (parsed_charstring_),
+      parsed_global_subrs (parsed_global_subrs_),
+      parsed_local_subrs (parsed_local_subrs_),
+      global_closure (global_closure_),
+      local_closure (local_closure_),
+      drop_hints (drop_hints_) {}
 
   parsed_cs_str_t *get_parsed_str_for_context (call_context_t &context)
   {
@@ -468,6 +467,7 @@ struct subr_remap_t : hb_inc_bimap_t
      * no optimization based on usage counts. fonttools doesn't appear doing that either.
      */
 
+    resize (closure->get_population ());
     hb_codepoint_t old_num = HB_SET_VALUE_INVALID;
     while (hb_set_next (closure, &old_num))
       add (old_num);
@@ -561,19 +561,21 @@ struct subr_subsetter_t
       hb_codepoint_t  glyph;
       if (!plan->old_gid_for_new_gid (i, &glyph))
 	continue;
-      const byte_str_t str = (*acc.charStrings)[glyph];
+      const hb_ubytes_t str = (*acc.charStrings)[glyph];
       unsigned int fd = acc.fdSelect->get_fd (glyph);
       if (unlikely (fd >= acc.fdCount))
 	return false;
 
-      cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp;
-      interp.env.init (str, acc, fd);
+      ENV env (str, acc, fd);
+      cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env);
 
-      subr_subset_param_t  param;
-      param.init (&parsed_charstrings[i],
-		  &parsed_global_subrs,  &parsed_local_subrs[fd],
-		  &closures.global_closure, &closures.local_closures[fd],
-		  plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
+      parsed_charstrings[i].alloc (str.length);
+      subr_subset_param_t  param (&parsed_charstrings[i],
+				  &parsed_global_subrs,
+				  &parsed_local_subrs[fd],
+				  &closures.global_closure,
+				  &closures.local_closures[fd],
+				  plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
 
       if (unlikely (!interp.interpret (param)))
 	return false;
@@ -593,11 +595,12 @@ struct subr_subsetter_t
 	unsigned int fd = acc.fdSelect->get_fd (glyph);
 	if (unlikely (fd >= acc.fdCount))
 	  return false;
-	subr_subset_param_t  param;
-	param.init (&parsed_charstrings[i],
-		    &parsed_global_subrs,  &parsed_local_subrs[fd],
-		    &closures.global_closure, &closures.local_closures[fd],
-                    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
+	subr_subset_param_t  param (&parsed_charstrings[i],
+				    &parsed_global_subrs,
+				    &parsed_local_subrs[fd],
+				    &closures.global_closure,
+				    &closures.local_closures[fd],
+				    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
 
 	drop_hints_param_t  drop;
 	if (drop_hints_in_str (parsed_charstrings[i], param, drop))
@@ -618,11 +621,12 @@ struct subr_subsetter_t
 	unsigned int fd = acc.fdSelect->get_fd (glyph);
 	if (unlikely (fd >= acc.fdCount))
 	  return false;
-	subr_subset_param_t  param;
-	param.init (&parsed_charstrings[i],
-		    &parsed_global_subrs,  &parsed_local_subrs[fd],
-		    &closures.global_closure, &closures.local_closures[fd],
-                    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
+	subr_subset_param_t  param (&parsed_charstrings[i],
+				    &parsed_global_subrs,
+				    &parsed_local_subrs[fd],
+				    &closures.global_closure,
+				    &closures.local_closures[fd],
+				    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
 	collect_subr_refs_in_str (parsed_charstrings[i], param);
       }
     }
@@ -849,9 +853,10 @@ struct subr_subsetter_t
 
   bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff) const
   {
-    buff.init ();
+    unsigned count = str.get_count ();
     str_encoder_t  encoder (buff);
     encoder.reset ();
+    buff.alloc (count * 3);
     /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
      * re-insert it at the beginning of charstreing */
     if (str.has_prefix () && str.is_hint_dropped ())
@@ -860,7 +865,7 @@ struct subr_subsetter_t
       if (str.prefix_op () != OpCode_Invalid)
 	encoder.encode_op (str.prefix_op ());
     }
-    for (unsigned int i = 0; i < str.get_count(); i++)
+    for (unsigned int i = 0; i < count; i++)
     {
       const parsed_cs_op_t  &opstr = str.values[i];
       if (!opstr.for_drop () && !opstr.for_skip ())
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc
index 35fecd67bc501ab02bea37ec0729a0ff9dae3479..52bb13d320bb69d3c375cfcd414a323d57addd24 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc
@@ -169,7 +169,7 @@ struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<cff1_top_dic
 	  supp_op.op = op;
 	  if ( unlikely (!(opstr.str.length >= opstr.last_arg_offset + 3)))
 	    return_trace (false);
-	  supp_op.str = byte_str_t (&opstr.str + opstr.last_arg_offset, opstr.str.length - opstr.last_arg_offset);
+	  supp_op.str = hb_ubytes_t (&opstr.str + opstr.last_arg_offset, opstr.str.length - opstr.last_arg_offset);
 	  return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) &&
 			UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) &&
 			copy_opstr (c, supp_op));
@@ -270,13 +270,13 @@ struct range_list_t : hb_vector_t<code_pair_t>
   /* replace the first glyph ID in the "glyph" field each range with a nLeft value */
   bool complete (unsigned int last_glyph)
   {
-    bool  two_byte = false;
-    for (unsigned int i = (*this).length; i > 0; i--)
+    bool two_byte = false;
+    unsigned count = this->length;
+    for (unsigned int i = count; i; i--)
     {
-      code_pair_t &pair = (*this)[i - 1];
-      unsigned int  nLeft = last_glyph - pair.glyph - 1;
-      if (nLeft >= 0x100)
-	two_byte = true;
+      code_pair_t &pair = arrayZ[i - 1];
+      unsigned int nLeft = last_glyph - pair.glyph - 1;
+      two_byte |= nLeft >= 0x100;
       last_glyph = pair.glyph;
       pair.glyph = nLeft;
     }
@@ -442,6 +442,9 @@ struct cff_subset_plan {
       return;
     }
 
+    bool use_glyph_to_sid_map = plan->num_output_glyphs () > plan->source->get_num_glyphs () / 8.;
+    hb_map_t *glyph_to_sid_map = use_glyph_to_sid_map ? acc.create_glyph_to_sid_map () : nullptr;
+
     unsigned int glyph;
     for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
     {
@@ -451,7 +454,7 @@ struct cff_subset_plan {
 	/* Retain the SID for the old missing glyph ID */
 	old_glyph = glyph;
       }
-      sid = acc.glyph_to_sid (old_glyph);
+      sid = glyph_to_sid_map ? glyph_to_sid_map->get (old_glyph) : acc.glyph_to_sid (old_glyph);
 
       if (!acc.is_CID ())
 	sid = sidmap.add (sid);
@@ -464,6 +467,9 @@ struct cff_subset_plan {
       last_sid = sid;
     }
 
+    if (glyph_to_sid_map)
+      hb_map_destroy (glyph_to_sid_map);
+
     bool two_byte = subset_charset_ranges.complete (glyph);
 
     size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc
index 92dd6b1d2ca9b731c4a2dd6d5eeb1057525a9c82..08e820efcfbda9d8f9aa8674829bf3101e26ec0f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc
@@ -67,9 +67,9 @@ struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
   }
 };
 
-struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t>
+struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t>
 {
-  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     switch (op)
     {
@@ -97,7 +97,7 @@ struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatte
     }
   }
 
-  static void flush_args (cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     for (unsigned int i = 0; i < env.argStack.get_count ();)
     {
@@ -122,7 +122,7 @@ struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatte
     SUPER::flush_args (env, param);
   }
 
-  static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     /* flatten the default values */
     str_encoder_t  encoder (param.flatStr);
@@ -149,7 +149,7 @@ struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatte
     encoder.encode_op (OpCode_blendcs);
   }
 
-  static void flush_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     switch (op)
     {
@@ -163,13 +163,13 @@ struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatte
   }
 
   private:
-  typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> SUPER;
-  typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t, flatten_param_t> CSOPSET;
+  typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER;
+  typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t<blend_arg_t>, flatten_param_t> CSOPSET;
 };
 
-struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t>
+struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t>
 {
-  static void process_op (op_code_t op, cff2_cs_interp_env_t &env, subr_subset_param_t& param)
+  static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param)
   {
     switch (op) {
 
@@ -201,7 +201,7 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t
 
   protected:
   static void process_call_subr (op_code_t op, cs_type_t type,
-				 cff2_cs_interp_env_t &env, subr_subset_param_t& param,
+				 cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param,
 				 cff2_biased_subrs_t& subrs, hb_set_t *closure)
   {
     byte_str_ref_t    str_ref = env.str_ref;
@@ -212,15 +212,15 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t
   }
 
   private:
-  typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> SUPER;
+  typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER;
 };
 
-struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_subr_subset_t>
+struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_subr_subset_t>
 {
   cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
     : subr_subsetter_t (acc_, plan_) {}
 
-  static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
+  static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
   {
     /* vsindex is inserted at the beginning of the charstring as necessary */
     if (env.seen_vsindex ())
@@ -245,7 +245,7 @@ struct cff2_subset_plan {
     if (desubroutinize)
     {
       /* Flatten global & local subrs */
-      subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t>
+      subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t>
 		    flattener(acc, plan);
       if (!flattener.flatten (subset_charstrings))
 	return false;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-input.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-input.cc
index 4885280996771d4f4bd945f56e7d307411c615f7..028ddf9035e034afc73310aaae0fd821b5e13a4e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-input.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-input.cc
@@ -140,7 +140,20 @@ hb_subset_input_create_or_fail (void)
     HB_TAG ('r', 't', 'l', 'a'),
     HB_TAG ('r', 't', 'l', 'm'),
 
-    //Complex shapers
+    //random
+    HB_TAG ('r', 'a', 'n', 'd'),
+
+    //justify
+    HB_TAG ('j', 'a', 'l', 't'), // HarfBuzz doesn't use; others might
+
+    //private
+    HB_TAG ('H', 'a', 'r', 'f'),
+    HB_TAG ('H', 'A', 'R', 'F'),
+    HB_TAG ('B', 'u', 'z', 'z'),
+    HB_TAG ('B', 'U', 'Z', 'Z'),
+
+    //shapers
+
     //arabic
     HB_TAG ('i', 'n', 'i', 't'),
     HB_TAG ('m', 'e', 'd', 'i'),
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc
index 74b7e3977c002c1fb4fdd401533a21170e44f289..4e3bb1d47747629549996df75bfcea5bef881bbe 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc
@@ -41,9 +41,9 @@
 #include "hb-ot-math-table.hh"
 
 using OT::Layout::GSUB::GSUB;
+using OT::Layout::GPOS;
 
-
-typedef hb_hashmap_t<unsigned, hb_set_t *> script_langsys_map;
+typedef hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> script_langsys_map;
 #ifndef HB_NO_SUBSET_CFF
 static inline void
 _add_cff_seac_components (const OT::cff1::accelerator_t &cff,
@@ -204,7 +204,7 @@ static inline void
 				     hb_map_t  *layout_variation_idx_map)
 {
   hb_blob_ptr_t<OT::GDEF> gdef = hb_sanitize_context_t ().reference_table<OT::GDEF> (face);
-  hb_blob_ptr_t<OT::GPOS> gpos = hb_sanitize_context_t ().reference_table<OT::GPOS> (face);
+  hb_blob_ptr_t<GPOS> gpos = hb_sanitize_context_t ().reference_table<GPOS> (face);
 
   if (!gdef->has_data ())
   {
@@ -279,12 +279,7 @@ static inline void
 _remove_invalid_gids (hb_set_t *glyphs,
 		      unsigned int num_glyphs)
 {
-  hb_codepoint_t gid = HB_SET_VALUE_INVALID;
-  while (glyphs->next (&gid))
-  {
-    if (gid >= num_glyphs)
-      glyphs->del (gid);
-  }
+  glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID);
 }
 
 static void
@@ -294,12 +289,13 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
 {
   OT::cmap::accelerator_t cmap (plan->source);
 
-  constexpr static const int size_threshold = 4096;
-
+  unsigned size_threshold = plan->source->get_num_glyphs ();
   if (glyphs->is_empty () && unicodes->get_population () < size_threshold)
   {
-    /* This is the fast path if it's anticipated that size of unicodes
-     * is << than the number of codepoints in the font. */
+    // This is approach to collection is faster, but can only be used  if glyphs
+    // are not being explicitly added to the subset and the input unicodes set is
+    // not excessively large (eg. an inverted set).
+    plan->unicode_to_new_gid_list.alloc (unicodes->get_population ());
     for (hb_codepoint_t cp : *unicodes)
     {
       hb_codepoint_t gid;
@@ -310,27 +306,32 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
       }
 
       plan->codepoint_to_glyph->set (cp, gid);
+      plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
     }
   }
   else
   {
+    // This approach is slower, but can handle adding in glyphs to the subset and will match
+    // them with cmap entries.
     hb_map_t unicode_glyphid_map;
-    cmap.collect_mapping (hb_set_get_empty (), &unicode_glyphid_map);
+    hb_set_t cmap_unicodes;
+    cmap.collect_mapping (&cmap_unicodes, &unicode_glyphid_map);
+    plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
+                                                + glyphs->get_population (),
+                                                cmap_unicodes.get_population ()));
 
-    for (hb_pair_t<hb_codepoint_t, hb_codepoint_t> cp_gid :
-	 + unicode_glyphid_map.iter ())
+    for (hb_codepoint_t cp : cmap_unicodes)
     {
-      if (!unicodes->has (cp_gid.first) && !glyphs->has (cp_gid.second))
-	continue;
+      hb_codepoint_t gid = unicode_glyphid_map[cp];
+      if (!unicodes->has (cp) && !glyphs->has (gid))
+        continue;
 
-      plan->codepoint_to_glyph->set (cp_gid.first, cp_gid.second);
+      plan->codepoint_to_glyph->set (cp, gid);
+      plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
     }
 
     /* Add gids which where requested, but not mapped in cmap */
-    // TODO(garretrieger):
-    // Once https://github.com/harfbuzz/harfbuzz/issues/3169
-    // is implemented, this can be done with union and del_range
-    for (hb_codepoint_t gid : glyphs->iter ())
+    for (hb_codepoint_t gid : *glyphs)
     {
       if (gid >= plan->source->get_num_glyphs ())
 	break;
@@ -338,8 +339,41 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
     }
   }
 
-  + plan->codepoint_to_glyph->keys ()   | hb_sink (plan->unicodes);
-  + plan->codepoint_to_glyph->values () | hb_sink (plan->_glyphset_gsub);
+  auto &arr = plan->unicode_to_new_gid_list;
+  if (arr.length)
+  {
+    plan->unicodes->add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ));
+    plan->_glyphset_gsub->add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ));
+  }
+}
+
+#ifndef HB_COMPOSITE_OPERATIONS_PER_GLYPH
+#define HB_COMPOSITE_OPERATIONS_PER_GLYPH 64
+#endif
+
+static unsigned
+_glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
+			    hb_codepoint_t gid,
+			    hb_set_t *gids_to_retain,
+			    int operation_count,
+			    unsigned depth = 0)
+{
+  if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
+  if (unlikely (--operation_count < 0)) return operation_count;
+  /* Check if is already visited */
+  if (gids_to_retain->has (gid)) return operation_count;
+
+  gids_to_retain->add (gid);
+
+  for (auto item : glyf.glyph_for_gid (gid).get_composite_iterator ())
+    operation_count =
+      _glyf_add_gid_and_children (glyf,
+				  item.glyphIndex,
+				  gids_to_retain,
+				  operation_count,
+				  depth);
+
+  return operation_count;
 }
 
 static void
@@ -348,7 +382,7 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
 			  bool close_over_gpos,
 			  bool close_over_gdef)
 {
-  OT::glyf::accelerator_t glyf (plan->source);
+  OT::glyf_accelerator_t glyf (plan->source);
 #ifndef HB_NO_SUBSET_CFF
   OT::cff1::accelerator_t cff (plan->source);
 #endif
@@ -369,7 +403,7 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
         plan->gsub_langsys);
 
   if (close_over_gpos)
-    _closure_glyphs_lookups_features<OT::GPOS> (
+    _closure_glyphs_lookups_features<GPOS> (
         plan->source,
         plan->_glyphset_gsub,
         plan->layout_features,
@@ -388,16 +422,20 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
   _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
 
   hb_set_set (plan->_glyphset_colred, &cur_glyphset);
-  // Populate a full set of glyphs to retain by adding all referenced
-  // composite glyphs.
-  for (hb_codepoint_t gid : cur_glyphset.iter ())
-  {
-    glyf.add_gid_and_children (gid, plan->_glyphset);
+
+  /* Populate a full set of glyphs to retain by adding all referenced
+   * composite glyphs. */
+  if (glyf.has_data ())
+    for (hb_codepoint_t gid : cur_glyphset)
+      _glyf_add_gid_and_children (glyf, gid, plan->_glyphset,
+				  cur_glyphset.get_population () * HB_COMPOSITE_OPERATIONS_PER_GLYPH);
+  else
+    plan->_glyphset->union_ (cur_glyphset);
 #ifndef HB_NO_SUBSET_CFF
-    if (cff.is_valid ())
+  if (cff.is_valid ())
+    for (hb_codepoint_t gid : cur_glyphset)
       _add_cff_seac_components (cff, gid, plan->_glyphset);
 #endif
-  }
 
   _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ());
 
@@ -412,6 +450,20 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
 #endif
 }
 
+static void
+_create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
+                        const hb_map_t* glyph_map,
+                        hb_map_t* out)
+{
+  + hb_iter (glyph_set_gsub)
+  | hb_map ([&] (hb_codepoint_t gid) {
+    return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid,
+                                                      glyph_map->get (gid));
+  })
+  | hb_sink (out)
+  ;
+}
+
 static void
 _create_old_gid_to_new_gid_map (const hb_face_t *face,
 				bool		 retain_gids,
@@ -420,13 +472,19 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face,
 				hb_map_t	*reverse_glyph_map, /* OUT */
 				unsigned int	*num_glyphs /* OUT */)
 {
+  unsigned pop = all_gids_to_retain->get_population ();
+  reverse_glyph_map->resize (pop);
+  glyph_map->resize (pop);
+
   if (!retain_gids)
   {
     + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
     | hb_sink (reverse_glyph_map)
     ;
     *num_glyphs = reverse_glyph_map->get_population ();
-  } else {
+  }
+  else
+  {
     + hb_iter (all_gids_to_retain)
     | hb_map ([] (hb_codepoint_t _) {
 		return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
@@ -434,10 +492,9 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face,
     | hb_sink (reverse_glyph_map)
     ;
 
-    unsigned max_glyph =
-    + hb_iter (all_gids_to_retain)
-    | hb_reduce (hb_max, 0u)
-    ;
+    hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
+    hb_set_previous (all_gids_to_retain, &max_glyph);
+
     *num_glyphs = max_glyph + 1;
   }
 
@@ -485,6 +542,9 @@ hb_subset_plan_create_or_fail (hb_face_t	 *face,
   plan->successful = true;
   plan->flags = input->flags;
   plan->unicodes = hb_set_create ();
+
+  plan->unicode_to_new_gid_list.init ();
+
   plan->name_ids = hb_set_copy (input->sets.name_ids);
   _nameid_closure (face, plan->name_ids);
   plan->name_languages = hb_set_copy (input->sets.name_languages);
@@ -502,6 +562,7 @@ hb_subset_plan_create_or_fail (hb_face_t	 *face,
   plan->codepoint_to_glyph = hb_map_create ();
   plan->glyph_map = hb_map_create ();
   plan->reverse_glyph_map = hb_map_create ();
+  plan->glyph_map_gsub = hb_map_create ();
   plan->gsub_lookups = hb_map_create ();
   plan->gpos_lookups = hb_map_create ();
 
@@ -536,6 +597,19 @@ hb_subset_plan_create_or_fail (hb_face_t	 *face,
 				  plan->reverse_glyph_map,
 				  &plan->_num_output_glyphs);
 
+  _create_glyph_map_gsub (
+      plan->_glyphset_gsub,
+      plan->glyph_map,
+      plan->glyph_map_gsub);
+
+  // Now that we have old to new gid map update the unicode to new gid list.
+  for (unsigned i = 0; i < plan->unicode_to_new_gid_list.length; i++)
+  {
+    // Use raw array access for performance.
+    plan->unicode_to_new_gid_list.arrayZ[i].second =
+        plan->glyph_map->get(plan->unicode_to_new_gid_list.arrayZ[i].second);
+  }
+
   if (unlikely (plan->in_error ())) {
     hb_subset_plan_destroy (plan);
     return nullptr;
@@ -558,6 +632,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
   if (!hb_object_destroy (plan)) return;
 
   hb_set_destroy (plan->unicodes);
+  plan->unicode_to_new_gid_list.fini ();
   hb_set_destroy (plan->name_ids);
   hb_set_destroy (plan->name_languages);
   hb_set_destroy (plan->layout_features);
@@ -569,6 +644,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
   hb_map_destroy (plan->codepoint_to_glyph);
   hb_map_destroy (plan->glyph_map);
   hb_map_destroy (plan->reverse_glyph_map);
+  hb_map_destroy (plan->glyph_map_gsub);
   hb_set_destroy (plan->_glyphset);
   hb_set_destroy (plan->_glyphset_gsub);
   hb_set_destroy (plan->_glyphset_mathed);
@@ -584,9 +660,6 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
 
   if (plan->gsub_langsys)
   {
-    for (auto _ : plan->gsub_langsys->iter ())
-      hb_set_destroy (_.second);
-
     hb_object_destroy (plan->gsub_langsys);
     plan->gsub_langsys->fini_shallow ();
     hb_free (plan->gsub_langsys);
@@ -594,9 +667,6 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
 
   if (plan->gpos_langsys)
   {
-    for (auto _ : plan->gpos_langsys->iter ())
-      hb_set_destroy (_.second);
-
     hb_object_destroy (plan->gpos_langsys);
     plan->gpos_langsys->fini_shallow ();
     hb_free (plan->gpos_langsys);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh
index ab2c4c302c517f3cc0ac1eca9bd1ae4278bf8713..2aaf19c61de686a1676bb5a8620b836a3c5bd337 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh
@@ -44,6 +44,7 @@ struct hb_subset_plan_t
 
   // For each cp that we'd like to retain maps to the corresponding gid.
   hb_set_t *unicodes;
+  hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> unicode_to_new_gid_list;
 
   // name_ids we would like to retain
   hb_set_t *name_ids;
@@ -69,6 +70,7 @@ struct hb_subset_plan_t
   // Old -> New glyph id mapping
   hb_map_t *glyph_map;
   hb_map_t *reverse_glyph_map;
+  hb_map_t *glyph_map_gsub;
 
   // Plan is only good for a specific source/dest so keep them with it
   hb_face_t *source;
@@ -85,8 +87,8 @@ struct hb_subset_plan_t
   hb_map_t *gpos_lookups;
 
   //active langsys we'd like to retain
-  hb_hashmap_t<unsigned, hb_set_t *> *gsub_langsys;
-  hb_hashmap_t<unsigned, hb_set_t *> *gpos_langsys;
+  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *gsub_langsys;
+  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *gpos_langsys;
 
   //active features after removing redundant langsys and prune_features
   hb_map_t *gsub_features;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
index 4588268b762f678307cae918ac513004c0d06fe0..10c572c2f7108e0968c9618430026d0a53e035a0 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
@@ -56,6 +56,7 @@
 #include "hb-repacker.hh"
 
 using OT::Layout::GSUB::GSUB;
+using OT::Layout::GPOS;
 
 /**
  * SECTION:hb-subset
@@ -78,13 +79,117 @@ using OT::Layout::GSUB::GSUB;
  * retain glyph ids option and configure the subset to pass through the layout tables untouched.
  */
 
+/*
+ * The list of tables in the open type spec. Used to check for tables that may need handling
+ * if we are unable to list the tables in a face.
+ */
+static hb_tag_t known_tables[] {
+  HB_TAG ('a', 'v', 'a', 'r'),
+  HB_OT_TAG_BASE,
+  HB_OT_TAG_CBDT,
+  HB_OT_TAG_CBLC,
+  HB_OT_TAG_cff1,
+  HB_OT_TAG_cff2,
+  HB_OT_TAG_cmap,
+  HB_OT_TAG_COLR,
+  HB_OT_TAG_CPAL,
+  HB_TAG ('c', 'v', 'a', 'r'),
+  HB_TAG ('c', 'v', 't', ' '),
+  HB_TAG ('D', 'S', 'I', 'G'),
+  HB_TAG ('E', 'B', 'D', 'T'),
+  HB_TAG ('E', 'B', 'L', 'C'),
+  HB_TAG ('E', 'B', 'S', 'C'),
+  HB_TAG ('f', 'p', 'g', 'm'),
+  HB_TAG ('f', 'v', 'a', 'r'),
+  HB_TAG ('g', 'a', 's', 'p'),
+  HB_OT_TAG_GDEF,
+  HB_OT_TAG_glyf,
+  HB_OT_TAG_GPOS,
+  HB_OT_TAG_GSUB,
+  HB_OT_TAG_gvar,
+  HB_OT_TAG_hdmx,
+  HB_OT_TAG_head,
+  HB_OT_TAG_hhea,
+  HB_OT_TAG_hmtx,
+  HB_OT_TAG_HVAR,
+  HB_OT_TAG_JSTF,
+  HB_TAG ('k', 'e', 'r', 'n'),
+  HB_OT_TAG_loca,
+  HB_TAG ('L', 'T', 'S', 'H'),
+  HB_OT_TAG_MATH,
+  HB_OT_TAG_maxp,
+  HB_TAG ('M', 'E', 'R', 'G'),
+  HB_TAG ('m', 'e', 't', 'a'),
+  HB_TAG ('M', 'V', 'A', 'R'),
+  HB_TAG ('P', 'C', 'L', 'T'),
+  HB_OT_TAG_post,
+  HB_TAG ('p', 'r', 'e', 'p'),
+  HB_OT_TAG_sbix,
+  HB_TAG ('S', 'T', 'A', 'T'),
+  HB_TAG ('S', 'V', 'G', ' '),
+  HB_TAG ('V', 'D', 'M', 'X'),
+  HB_OT_TAG_vhea,
+  HB_OT_TAG_vmtx,
+  HB_OT_TAG_VORG,
+  HB_OT_TAG_VVAR,
+  HB_OT_TAG_name,
+  HB_OT_TAG_OS2
+};
+
+static bool _table_is_empty (const hb_face_t *face, hb_tag_t tag)
+{
+  hb_blob_t* blob = hb_face_reference_table (face, tag);
+  bool result = (blob == hb_blob_get_empty ());
+  hb_blob_destroy (blob);
+  return result;
+}
+
+static unsigned int
+_get_table_tags (const hb_subset_plan_t* plan,
+                 unsigned int  start_offset,
+                 unsigned int *table_count, /* IN/OUT */
+                 hb_tag_t     *table_tags /* OUT */)
+{
+  unsigned num_tables = hb_face_get_table_tags (plan->source, 0, nullptr, nullptr);
+  if (num_tables)
+    return hb_face_get_table_tags (plan->source, start_offset, table_count, table_tags);
+
+  // If face has 0 tables associated with it, assume that it was built from
+  // hb_face_create_tables and thus is unable to list its tables. Fallback to
+  // checking each table type we can handle for existence instead.
+  auto it =
+      hb_concat (
+          + hb_array (known_tables)
+          | hb_filter ([&] (hb_tag_t tag) {
+            return !_table_is_empty (plan->source, tag) && !plan->no_subset_tables->has (tag);
+          })
+          | hb_map ([] (hb_tag_t tag) -> hb_tag_t { return tag; }),
+
+          plan->no_subset_tables->iter ()
+          | hb_filter([&] (hb_tag_t tag) {
+            return !_table_is_empty (plan->source, tag);
+          }));
+
+  it += start_offset;
+
+  unsigned num_written = 0;
+  while (bool (it) && num_written < *table_count)
+    table_tags[num_written++] = *it++;
+
+  *table_count = num_written;
+  return num_written;
+}
+
+
 static unsigned
-_plan_estimate_subset_table_size (hb_subset_plan_t *plan, unsigned table_len)
+_plan_estimate_subset_table_size (hb_subset_plan_t *plan,
+				  unsigned table_len,
+				  bool same_size)
 {
   unsigned src_glyphs = plan->source->get_num_glyphs ();
   unsigned dst_glyphs = plan->glyphset ()->get_population ();
 
-  if (unlikely (!src_glyphs))
+  if (unlikely (!src_glyphs) || same_size)
     return 512 + table_len;
 
   return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
@@ -123,7 +228,6 @@ static
 bool
 _try_subset (const TableType *table,
              hb_vector_t<char>* buf,
-             unsigned buf_size,
              hb_subset_context_t* c /* OUT */)
 {
   c->serializer->start_serialize<TableType> ();
@@ -136,7 +240,8 @@ _try_subset (const TableType *table,
     return needed;
   }
 
-  buf_size += (buf_size >> 1) + 32;
+  unsigned buf_size = buf->allocated;
+  buf_size = buf_size * 2 + 16;
   DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.",
              HB_UNTAG (c->table_tag), buf_size);
 
@@ -147,13 +252,13 @@ _try_subset (const TableType *table,
     return needed;
   }
 
-  c->serializer->reset (buf->arrayZ, buf_size);
-  return _try_subset (table, buf, buf_size, c);
+  c->serializer->reset (buf->arrayZ, buf->allocated);
+  return _try_subset (table, buf, c);
 }
 
 template<typename TableType>
 static bool
-_subset (hb_subset_plan_t *plan)
+_subset (hb_subset_plan_t *plan, hb_vector_t<char> &buf)
 {
   hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
   const TableType *table = source_blob->as<TableType> ();
@@ -167,10 +272,13 @@ _subset (hb_subset_plan_t *plan)
     return false;
   }
 
-  hb_vector_t<char> buf;
-  /* TODO Not all tables are glyph-related.  'name' table size for example should not be
-   * affected by number of glyphs.  Accommodate that. */
-  unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob->length);
+  /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's
+   * because those are expensive to subset, so giving them more room is fine. */
+  bool same_size_table = TableType::tableTag == HB_OT_TAG_GSUB ||
+			 TableType::tableTag == HB_OT_TAG_GPOS ||
+			 TableType::tableTag == HB_OT_TAG_name;
+
+  unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob->length, same_size_table);
   DEBUG_MSG (SUBSET, nullptr,
              "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
   if (unlikely (!buf.alloc (buf_size)))
@@ -181,10 +289,10 @@ _subset (hb_subset_plan_t *plan)
   }
 
   bool needed = false;
-  hb_serialize_context_t serializer (buf.arrayZ, buf_size);
+  hb_serialize_context_t serializer (buf.arrayZ, buf.allocated);
   {
     hb_subset_context_t c (source_blob, plan, &serializer, tag);
-    needed = _try_subset (table, &buf, buf_size, &c);
+    needed = _try_subset (table, &buf, &c);
   }
   hb_blob_destroy (source_blob);
 
@@ -219,9 +327,17 @@ _subset (hb_subset_plan_t *plan)
 static bool
 _is_table_present (hb_face_t *source, hb_tag_t tag)
 {
+
+  if (!hb_face_get_table_tags (source, 0, nullptr, nullptr)) {
+    // If face has 0 tables associated with it, assume that it was built from
+    // hb_face_create_tables and thus is unable to list its tables. Fallback to
+    // checking if the blob associated with tag is empty.
+    return !_table_is_empty (source, tag);
+  }
+
   hb_tag_t table_tags[32];
   unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
-  while ((hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables))
+  while (((void) hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables))
   {
     for (unsigned i = 0; i < num_tables; ++i)
       if (table_tags[i] == tag)
@@ -274,7 +390,9 @@ _passthrough (hb_subset_plan_t *plan, hb_tag_t tag)
 }
 
 static bool
-_subset_table (hb_subset_plan_t *plan, hb_tag_t tag)
+_subset_table (hb_subset_plan_t *plan,
+	       hb_vector_t<char> &buf,
+	       hb_tag_t tag)
 {
   if (plan->no_subset_tables->has (tag)) {
     return _passthrough (plan, tag);
@@ -283,42 +401,42 @@ _subset_table (hb_subset_plan_t *plan, hb_tag_t tag)
   DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag));
   switch (tag)
   {
-  case HB_OT_TAG_glyf: return _subset<const OT::glyf> (plan);
-  case HB_OT_TAG_hdmx: return _subset<const OT::hdmx> (plan);
-  case HB_OT_TAG_name: return _subset<const OT::name> (plan);
+  case HB_OT_TAG_glyf: return _subset<const OT::glyf> (plan, buf);
+  case HB_OT_TAG_hdmx: return _subset<const OT::hdmx> (plan, buf);
+  case HB_OT_TAG_name: return _subset<const OT::name> (plan, buf);
   case HB_OT_TAG_head:
     if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf))
       return true; /* skip head, handled by glyf */
-    return _subset<const OT::head> (plan);
+    return _subset<const OT::head> (plan, buf);
   case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */
-  case HB_OT_TAG_hmtx: return _subset<const OT::hmtx> (plan);
+  case HB_OT_TAG_hmtx: return _subset<const OT::hmtx> (plan, buf);
   case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */
-  case HB_OT_TAG_vmtx: return _subset<const OT::vmtx> (plan);
-  case HB_OT_TAG_maxp: return _subset<const OT::maxp> (plan);
-  case HB_OT_TAG_sbix: return _subset<const OT::sbix> (plan);
+  case HB_OT_TAG_vmtx: return _subset<const OT::vmtx> (plan, buf);
+  case HB_OT_TAG_maxp: return _subset<const OT::maxp> (plan, buf);
+  case HB_OT_TAG_sbix: return _subset<const OT::sbix> (plan, buf);
   case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */
-  case HB_OT_TAG_cmap: return _subset<const OT::cmap> (plan);
-  case HB_OT_TAG_OS2 : return _subset<const OT::OS2 > (plan);
-  case HB_OT_TAG_post: return _subset<const OT::post> (plan);
-  case HB_OT_TAG_COLR: return _subset<const OT::COLR> (plan);
-  case HB_OT_TAG_CPAL: return _subset<const OT::CPAL> (plan);
-  case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan);
+  case HB_OT_TAG_cmap: return _subset<const OT::cmap> (plan, buf);
+  case HB_OT_TAG_OS2 : return _subset<const OT::OS2 > (plan, buf);
+  case HB_OT_TAG_post: return _subset<const OT::post> (plan, buf);
+  case HB_OT_TAG_COLR: return _subset<const OT::COLR> (plan, buf);
+  case HB_OT_TAG_CPAL: return _subset<const OT::CPAL> (plan, buf);
+  case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan, buf);
   case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */
-  case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan);
+  case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan, buf);
 
 #ifndef HB_NO_SUBSET_CFF
-  case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan);
-  case HB_OT_TAG_cff2: return _subset<const OT::cff2> (plan);
-  case HB_OT_TAG_VORG: return _subset<const OT::VORG> (plan);
+  case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan, buf);
+  case HB_OT_TAG_cff2: return _subset<const OT::cff2> (plan, buf);
+  case HB_OT_TAG_VORG: return _subset<const OT::VORG> (plan, buf);
 #endif
 
 #ifndef HB_NO_SUBSET_LAYOUT
-  case HB_OT_TAG_GDEF: return _subset<const OT::GDEF> (plan);
-  case HB_OT_TAG_GSUB: return _subset<const GSUB> (plan);
-  case HB_OT_TAG_GPOS: return _subset<const OT::GPOS> (plan);
-  case HB_OT_TAG_gvar: return _subset<const OT::gvar> (plan);
-  case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan);
-  case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan);
+  case HB_OT_TAG_GDEF: return _subset<const OT::GDEF> (plan, buf);
+  case HB_OT_TAG_GSUB: return _subset<const GSUB> (plan, buf);
+  case HB_OT_TAG_GPOS: return _subset<const GPOS> (plan, buf);
+  case HB_OT_TAG_gvar: return _subset<const OT::gvar> (plan, buf);
+  case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan, buf);
+  case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan, buf);
 #endif
 
   default:
@@ -379,19 +497,22 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
   bool success = true;
   hb_tag_t table_tags[32];
   unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
-  while ((hb_face_get_table_tags (plan->source, offset, &num_tables, table_tags), num_tables))
+  hb_vector_t<char> buf;
+  buf.alloc (4096 - 16);
+
+  while (((void) _get_table_tags (plan, offset, &num_tables, table_tags), num_tables))
   {
     for (unsigned i = 0; i < num_tables; ++i)
     {
       hb_tag_t tag = table_tags[i];
       if (_should_drop_table (plan, tag) && !tags_set.has (tag)) continue;
       tags_set.add (tag);
-      success = _subset_table (plan, tag);
+      success = _subset_table (plan, buf, tag);
       if (unlikely (!success)) goto end;
     }
     offset += num_tables;
   }
-end:
 
+end:
   return success ? hb_face_reference (plan->dest) : nullptr;
 }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ucd-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ucd-table.hh
index 1a4c89c17fcdea632ddbe6768e036ac2fd4c8f8e..9e76ca460b201d99d1e111235627a593c2f31f96 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ucd-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ucd-table.hh
@@ -13,7 +13,7 @@
 #include "hb.hh"
 
 static const hb_script_t
-_hb_ucd_sc_map[162] =
+_hb_ucd_sc_map[163] =
 {
                    HB_SCRIPT_COMMON,              HB_SCRIPT_INHERITED,
                   HB_SCRIPT_UNKNOWN,                 HB_SCRIPT_ARABIC,
@@ -96,6 +96,7 @@ _hb_ucd_sc_map[162] =
                    HB_SCRIPT_YEZIDI,           HB_SCRIPT_CYPRO_MINOAN,
                HB_SCRIPT_OLD_UYGHUR,                 HB_SCRIPT_TANGSA,
                      HB_SCRIPT_TOTO,               HB_SCRIPT_VITHKUQI,
+                     HB_SCRIPT_MATH,
 };
 static const uint16_t
 _hb_ucd_dm1_p0_map[825] =
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.cc
index 83ead6398b3e979ab40a0a20ab4a7336388e4ec2..05f74b923776f76b0821310851cef5e69e5bbc93 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.cc
@@ -169,7 +169,7 @@ hb_unicode_funcs_get_default ()
 #endif
 
 /**
- * hb_unicode_funcs_create: (Xconstructor)
+ * hb_unicode_funcs_create:
  * @parent: (nullable): Parent Unicode-functions structure
  *
  * Creates a new #hb_unicode_funcs_t structure of Unicode functions.
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.hh
index 4c28bb0cdfa523dae06ecea35e14e8ad6acfd78e..39aaee5baa7f7cf16ea95e32a52bb778e111c303 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-unicode.hh
@@ -105,12 +105,9 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
   unsigned int
   modified_combining_class (hb_codepoint_t u)
   {
-    /* XXX This hack belongs to the USE shaper (for Tai Tham):
-     * Reorder SAKOT to ensure it comes after any tone marks. */
+    /* Reorder SAKOT to ensure it comes after any tone marks. */
     if (unlikely (u == 0x1A60u)) return 254;
-
-    /* XXX This hack belongs to the Tibetan shaper:
-     * Reorder PADMA to ensure it comes after any vowel marks. */
+    /* Reorder PADMA to ensure it comes after any vowel marks. */
     if (unlikely (u == 0x0FC6u)) return 254;
     /* Reorder TSA -PHRU to reorder before U+0F74 */
     if (unlikely (u == 0x0F39u)) return 127;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
index 6c7d32e49d87a2e179843a85ae81910d06495177..7b08e3b4d2796452cb6a36e95347e19072fbe157 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
@@ -29,6 +29,7 @@
 
 #include "hb.hh"
 #include "hb-array.hh"
+#include "hb-meta.hh"
 #include "hb-null.hh"
 
 
@@ -42,6 +43,7 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   using c_array_t = typename std::conditional<sorted, hb_sorted_array_t<const Type>, hb_array_t<const Type>>::type;
 
   hb_vector_t () = default;
+  hb_vector_t (std::nullptr_t) : hb_vector_t () {}
   hb_vector_t (std::initializer_list<Type> lst) : hb_vector_t ()
   {
     alloc (lst.size ());
@@ -59,7 +61,8 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   hb_vector_t (const hb_vector_t &o) : hb_vector_t ()
   {
     alloc (o.length);
-    hb_copy (o, *this);
+    if (unlikely (in_error ())) return;
+    copy_vector (o);
   }
   hb_vector_t (hb_vector_t &&o)
   {
@@ -70,9 +73,8 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   }
   ~hb_vector_t () { fini (); }
 
-  private:
-  int allocated = 0; /* == -1 means allocation failed. */
   public:
+  int allocated = 0; /* == -1 means allocation failed. */
   unsigned int length = 0;
   public:
   Type *arrayZ = nullptr;
@@ -108,7 +110,10 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   {
     reset ();
     alloc (o.length);
-    hb_copy (o, *this);
+    if (unlikely (in_error ())) return *this;
+
+    copy_vector (o);
+
     return *this;
   }
   hb_vector_t& operator = (hb_vector_t &&o)
@@ -184,12 +189,14 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   {
     if (unlikely (!resize (length + 1)))
       return &Crap (Type);
-    return &arrayZ[length - 1];
+    return std::addressof (arrayZ[length - 1]);
   }
-  template <typename T>
+  template <typename T,
+	    typename T2 = Type,
+	    hb_enable_if (!std::is_copy_constructible<T2>::value &&
+			  std::is_copy_assignable<T>::value)>
   Type *push (T&& v)
   {
-    /* TODO Emplace? */
     Type *p = push ();
     if (p == &Crap (Type))
       // If push failed to allocate then don't copy v, since this may cause
@@ -199,18 +206,34 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
     *p = std::forward<T> (v);
     return p;
   }
+  template <typename T,
+	    typename T2 = Type,
+	    hb_enable_if (std::is_copy_constructible<T2>::value)>
+  Type *push (T&& v)
+  {
+    if (unlikely (!alloc (length + 1)))
+      // If push failed to allocate then don't copy v, since this may cause
+      // the created copy to leak memory since we won't have stored a
+      // reference to it.
+      return &Crap (Type);
+
+    /* Emplace. */
+    length++;
+    Type *p = std::addressof (arrayZ[length - 1]);
+    return new (p) Type (std::forward<T> (v));
+  }
 
   bool in_error () const { return allocated < 0; }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (hb_is_trivially_copy_assignable(T))>
   Type *
   realloc_vector (unsigned new_allocated)
   {
     return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (!hb_is_trivially_copy_assignable(T))>
   Type *
   realloc_vector (unsigned new_allocated)
   {
@@ -230,8 +253,7 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_constructible<T>::value ||
-			  !std::is_default_constructible<T>::value)>
+	    hb_enable_if (hb_is_trivially_constructible(T))>
   void
   grow_vector (unsigned size)
   {
@@ -239,8 +261,7 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
     length = size;
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_constructible<T>::value &&
-			   std::is_default_constructible<T>::value)>
+	    hb_enable_if (!hb_is_trivially_constructible(T))>
   void
   grow_vector (unsigned size)
   {
@@ -252,14 +273,52 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_destructible<T>::value)>
+	    hb_enable_if (hb_is_trivially_copyable (T))>
+  void
+  copy_vector (const hb_vector_t &other)
+  {
+    length = other.length;
+    hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size);
+  }
+  template <typename T = Type,
+	    hb_enable_if (!hb_is_trivially_copyable (T) &&
+			   std::is_copy_constructible<T>::value)>
+  void
+  copy_vector (const hb_vector_t &other)
+  {
+    length = 0;
+    while (length < other.length)
+    {
+      length++;
+      new (std::addressof (arrayZ[length - 1])) Type (other.arrayZ[length - 1]);
+    }
+  }
+  template <typename T = Type,
+	    hb_enable_if (!hb_is_trivially_copyable (T) &&
+			  !std::is_copy_constructible<T>::value &&
+			  std::is_default_constructible<T>::value &&
+			  std::is_copy_assignable<T>::value)>
+  void
+  copy_vector (const hb_vector_t &other)
+  {
+    length = 0;
+    while (length < other.length)
+    {
+      length++;
+      new (std::addressof (arrayZ[length - 1])) Type ();
+      arrayZ[length - 1] = other.arrayZ[length - 1];
+    }
+  }
+
+  template <typename T = Type,
+	    hb_enable_if (hb_is_trivially_destructible(T))>
   void
   shrink_vector (unsigned size)
   {
     length = size;
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_destructible<T>::value)>
+	    hb_enable_if (!hb_is_trivially_destructible(T))>
   void
   shrink_vector (unsigned size)
   {
@@ -271,7 +330,7 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (hb_is_trivially_copy_assignable(T))>
   void
   shift_down_vector (unsigned i)
   {
@@ -280,7 +339,7 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
 	     (length - i) * sizeof (Type));
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (!hb_is_trivially_copy_assignable(T))>
   void
   shift_down_vector (unsigned i)
   {
@@ -341,7 +400,7 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   Type pop ()
   {
     if (!length) return Null (Type);
-    Type v = std::move (arrayZ[length - 1]);
+    Type v = arrayZ[length - 1];
     arrayZ[length - 1].~Type ();
     length--;
     return v;
@@ -351,8 +410,8 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty
   {
     if (unlikely (i >= length))
       return;
-    arrayZ[i].~Type ();
     shift_down_vector (i + 1);
+    arrayZ[length - 1].~Type ();
     length--;
   }
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb.hh
index b9f5f71415ef718a014895209bed9d0f1a19fa6a..8ec638a2b43133b282fd9446c222a64837774945 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb.hh
@@ -29,7 +29,6 @@
 #ifndef HB_HH
 #define HB_HH
 
-
 #ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC
 #ifdef _MSC_VER
 #pragma warning( disable: 4068 ) /* Unknown pragma */
@@ -65,6 +64,7 @@
 #pragma GCC diagnostic error   "-Wbitwise-instead-of-logical"
 #pragma GCC diagnostic error   "-Wcast-align"
 #pragma GCC diagnostic error   "-Wcast-function-type"
+#pragma GCC diagnostic error   "-Wcomma"
 #pragma GCC diagnostic error   "-Wdelete-non-virtual-dtor"
 #pragma GCC diagnostic error   "-Wembedded-directive"
 #pragma GCC diagnostic error   "-Wextra-semi-stmt"
@@ -183,7 +183,7 @@
 #include <cassert>
 #include <cfloat>
 #include <climits>
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
 # define _USE_MATH_DEFINES
 #endif
 #include <cmath>
@@ -470,6 +470,7 @@ static_assert ((sizeof (hb_var_int_t) == 4), "");
 /* Headers we include for everyone.  Keep topologically sorted by dependency.
  * They express dependency amongst themselves, but no other file should include
  * them directly.*/
+#include "hb-cplusplus.hh"
 #include "hb-meta.hh"
 #include "hb-mutex.hh"
 #include "hb-number.hh"
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/meson.build b/source/libs/harfbuzz/harfbuzz-src/src/meson.build
index affb40fdf41229cfa087015e35aa34f779fea961..b4fc6d408074335e2a91284ba8b21dd5be16ad39 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/meson.build
+++ b/source/libs/harfbuzz/harfbuzz-src/src/meson.build
@@ -92,6 +92,15 @@ hb_base_sources = files(
   'hb-ot-layout-gdef-table.hh',
   'hb-ot-layout-gpos-table.hh',
   'hb-ot-layout-gsub-table.hh',
+  'OT/glyf/glyf.hh',
+  'OT/glyf/glyf-helpers.hh',
+  'OT/glyf/loca.hh',
+  'OT/glyf/path-builder.hh',
+  'OT/glyf/Glyph.hh',
+  'OT/glyf/GlyphHeader.hh',
+  'OT/glyf/SimpleGlyph.hh',
+  'OT/glyf/CompositeGlyph.hh',
+  'OT/glyf/SubsetGlyph.hh',
   'OT/Layout/GSUB/Common.hh',
   'OT/Layout/GSUB/Sequence.hh',
   'OT/Layout/GSUB/SingleSubstFormat1.hh',
@@ -114,6 +123,35 @@ hb_base_sources = files(
   'OT/Layout/GSUB/SubstLookupSubTable.hh',
   'OT/Layout/GSUB/SubstLookup.hh',
   'OT/Layout/GSUB/GSUB.hh',
+  'OT/Layout/GPOS.hh',
+  'OT/Layout/GPOS/CursivePosFormat1.hh',
+  'OT/Layout/GPOS/MarkLigPos.hh',
+  'OT/Layout/GPOS/PairPos.hh',
+  'OT/Layout/GPOS/Anchor.hh',
+  'OT/Layout/GPOS/AnchorFormat1.hh',
+  'OT/Layout/GPOS/MarkLigPosFormat1.hh',
+  'OT/Layout/GPOS/PairPosFormat1.hh',
+  'OT/Layout/GPOS/ExtensionPos.hh',
+  'OT/Layout/GPOS/ChainContextPos.hh',
+  'OT/Layout/GPOS/Common.hh',
+  'OT/Layout/GPOS/ValueFormat.hh',
+  'OT/Layout/GPOS/AnchorMatrix.hh',
+  'OT/Layout/GPOS/MarkBasePosFormat1.hh',
+  'OT/Layout/GPOS/AnchorFormat3.hh',
+  'OT/Layout/GPOS/PosLookup.hh',
+  'OT/Layout/GPOS/MarkMarkPos.hh',
+  'OT/Layout/GPOS/PairPosFormat2.hh',
+  'OT/Layout/GPOS/MarkBasePos.hh',
+  'OT/Layout/GPOS/MarkMarkPosFormat1.hh',
+  'OT/Layout/GPOS/SinglePos.hh',
+  'OT/Layout/GPOS/MarkArray.hh',
+  'OT/Layout/GPOS/CursivePos.hh',
+  'OT/Layout/GPOS/PosLookupSubTable.hh',
+  'OT/Layout/GPOS/MarkRecord.hh',
+  'OT/Layout/GPOS/AnchorFormat2.hh',
+  'OT/Layout/GPOS/ContextPos.hh',
+  'OT/Layout/GPOS/SinglePosFormat2.hh',
+  'OT/Layout/GPOS/SinglePosFormat1.hh',
   'hb-ot-layout-gsubgpos.hh',
   'hb-ot-layout-jstf-table.hh',
   'hb-ot-layout.cc',
@@ -135,30 +173,29 @@ hb_base_sources = files(
   'hb-ot-os2-unicode-ranges.hh',
   'hb-ot-post-macroman.hh',
   'hb-ot-post-table.hh',
-  'hb-ot-shape-complex-arabic-fallback.hh',
-  'hb-ot-shape-complex-arabic-joining-list.hh',
-  'hb-ot-shape-complex-arabic-table.hh',
-  'hb-ot-shape-complex-arabic-win1256.hh',
-  'hb-ot-shape-complex-arabic.cc',
-  'hb-ot-shape-complex-arabic.hh',
-  'hb-ot-shape-complex-default.cc',
-  'hb-ot-shape-complex-hangul.cc',
-  'hb-ot-shape-complex-hebrew.cc',
-  'hb-ot-shape-complex-indic-table.cc',
-  'hb-ot-shape-complex-indic.cc',
-  'hb-ot-shape-complex-indic.hh',
-  'hb-ot-shape-complex-khmer.cc',
-  'hb-ot-shape-complex-khmer.hh',
-  'hb-ot-shape-complex-myanmar.cc',
-  'hb-ot-shape-complex-myanmar.hh',
-  'hb-ot-shape-complex-syllabic.cc',
-  'hb-ot-shape-complex-syllabic.hh',
-  'hb-ot-shape-complex-thai.cc',
-  'hb-ot-shape-complex-use-table.hh',
-  'hb-ot-shape-complex-use.cc',
-  'hb-ot-shape-complex-vowel-constraints.cc',
-  'hb-ot-shape-complex-vowel-constraints.hh',
-  'hb-ot-shape-complex.hh',
+  'hb-ot-shaper-arabic-fallback.hh',
+  'hb-ot-shaper-arabic-joining-list.hh',
+  'hb-ot-shaper-arabic-pua.hh',
+  'hb-ot-shaper-arabic-table.hh',
+  'hb-ot-shaper-arabic-win1256.hh',
+  'hb-ot-shaper-arabic.cc',
+  'hb-ot-shaper-arabic.hh',
+  'hb-ot-shaper-default.cc',
+  'hb-ot-shaper-hangul.cc',
+  'hb-ot-shaper-hebrew.cc',
+  'hb-ot-shaper-indic-table.cc',
+  'hb-ot-shaper-indic.cc',
+  'hb-ot-shaper-indic.hh',
+  'hb-ot-shaper-khmer.cc',
+  'hb-ot-shaper-myanmar.cc',
+  'hb-ot-shaper-syllabic.cc',
+  'hb-ot-shaper-syllabic.hh',
+  'hb-ot-shaper-thai.cc',
+  'hb-ot-shaper-use-table.hh',
+  'hb-ot-shaper-use.cc',
+  'hb-ot-shaper-vowel-constraints.cc',
+  'hb-ot-shaper-vowel-constraints.hh',
+  'hb-ot-shaper.hh',
   'hb-ot-shape-fallback.cc',
   'hb-ot-shape-fallback.hh',
   'hb-ot-shape-normalize.cc',
@@ -206,19 +243,19 @@ hb_base_ragel_generated_sources = files(
   'hb-buffer-deserialize-json.hh',
   'hb-buffer-deserialize-text.hh',
   'hb-number-parser.hh',
-  'hb-ot-shape-complex-indic-machine.hh',
-  'hb-ot-shape-complex-khmer-machine.hh',
-  'hb-ot-shape-complex-myanmar-machine.hh',
-  'hb-ot-shape-complex-use-machine.hh',
+  'hb-ot-shaper-indic-machine.hh',
+  'hb-ot-shaper-khmer-machine.hh',
+  'hb-ot-shaper-myanmar-machine.hh',
+  'hb-ot-shaper-use-machine.hh',
 )
 hb_base_ragel_sources = [
   'hb-buffer-deserialize-json.rl',
   'hb-buffer-deserialize-text.rl',
   'hb-number-parser.rl',
-  'hb-ot-shape-complex-indic-machine.rl',
-  'hb-ot-shape-complex-khmer-machine.rl',
-  'hb-ot-shape-complex-myanmar-machine.rl',
-  'hb-ot-shape-complex-use-machine.rl',
+  'hb-ot-shaper-indic-machine.rl',
+  'hb-ot-shaper-khmer-machine.rl',
+  'hb-ot-shaper-myanmar-machine.rl',
+  'hb-ot-shaper-use-machine.rl',
 ]
 
 hb_base_headers = files(
@@ -227,6 +264,7 @@ hb_base_headers = files(
   'hb-blob.h',
   'hb-buffer.h',
   'hb-common.h',
+  'hb-cplusplus.hh',
   'hb-deprecated.h',
   'hb-draw.h',
   'hb-face.h',
@@ -324,7 +362,9 @@ if not has_ragel and get_option('ragel_subproject')
     has_ragel = true
 endif
 if not has_ragel
-  warning('You have to install ragel if you are going to develop HarfBuzz itself')
+  if not meson.is_subproject()
+    warning('You have to install ragel if you are going to develop HarfBuzz itself')
+  endif
 else
   ragel_helper = find_program('gen-ragel-artifacts.py')
   foreach rl : hb_base_ragel_sources
@@ -674,7 +714,7 @@ if have_gobject
       nsversion: '0.0',
       identifier_prefix: 'hb_',
       symbol_prefix: ['hb', 'hb_gobject'],
-      includes: ['GObject-2.0'],
+      includes: ['GObject-2.0', 'freetype2-2.0'],
       export_packages: ['harfbuzz-gobject'],
       header: 'hb-gobject.h',
       install: true,
@@ -715,6 +755,7 @@ if get_option('tests').enabled()
 
   env = environment()
   env.set('srcdir', meson.current_source_dir())
+  env.set('base_srcdir', meson.source_root())
   env.set('builddir', meson.current_build_dir())
   env.set('libs', meson.current_build_dir()) # TODO: Merge this with builddir after autotools removal
   HBSOURCES = []
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/test-algs.cc b/source/libs/harfbuzz/harfbuzz-src/src/test-algs.cc
index f8b8ff66682e3b1255bfe1e8b9a3815eb364a147..450a7c439b7700611518ee7f30602417dc6151f9 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/test-algs.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/test-algs.cc
@@ -26,6 +26,7 @@
 
 #include "hb.hh"
 #include "hb-algs.hh"
+#include "hb-set.hh"
 
 
 static char *
@@ -91,5 +92,19 @@ main (int argc, char **argv)
   assert (++hb_inc (x) == 3);
   assert (x == 3);
 
+  hb_set_t set1 {1};
+  hb_set_t set2 {2};
+
+  assert (hb_hash (set1) != hb_hash (set2));
+  assert (hb_hash (set1) == hb_hash (hb_set_t {1}));
+  assert (hb_hash (set1) != hb_hash (hb_set_t {}));
+  assert (hb_hash (set1) != hb_hash (hb_set_t {2}));
+  assert (hb_hash (set2) == hb_hash (hb_set_t {2}));
+
+  /* hb_hash, unlike std::hash, dereferences pointers. */
+  assert (hb_hash (set1) == hb_hash (&set1));
+  assert (hb_hash (set1) == hb_hash (hb::shared_ptr<hb_set_t> {hb_set_reference (&set1)}));
+  assert (hb_hash (set1) == hb_hash (hb::unique_ptr<hb_set_t> {hb_set_reference (&set1)}));
+
   return 0;
 }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc b/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc
index fd2b2f0e642b01140c43b4b6311c985ae9f3f165..4cb248bd40e8d8e9f5e39f296c3099b8cb145839 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc
@@ -20,15 +20,13 @@
  * 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"
 #include "hb-map.hh"
+#include "hb-set.hh"
 #include <string>
 
-static const std::string invalid{"invalid"};
-
 int
 main (int argc, char **argv)
 {
@@ -57,13 +55,21 @@ main (int argc, char **argv)
 
   /* Test move constructor. */
   {
-    hb_map_t v {hb_map_t {}};
+    hb_map_t s {};
+    s.set (1, 2);
+    hb_map_t v (std::move (s));
+    assert (s.get_population () == 0);
+    assert (v.get_population () == 1);
   }
 
   /* Test move assignment. */
   {
+    hb_map_t s {};
+    s.set (1, 2);
     hb_map_t v;
-    v = hb_map_t {};
+    v = std::move (s);
+    assert (s.get_population () == 0);
+    assert (v.get_population () == 1);
   }
 
   /* Test initializing from iterable. */
@@ -73,9 +79,15 @@ main (int argc, char **argv)
     s.set (1, 2);
     s.set (3, 4);
 
-    hb_map_t v (s);
+    hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> v (s);
+    hb_map_t v0 (v);
+    hb_map_t v1 (s);
+    hb_map_t v2 (std::move (s));
 
-    assert (v.get_population () == 2);
+    assert (s.get_population () == 0);
+    assert (v0.get_population () == 2);
+    assert (v1.get_population () == 2);
+    assert (v2.get_population () == 2);
   }
 
   /* Test call fini() twice. */
@@ -110,19 +122,19 @@ main (int argc, char **argv)
 
   /* Test class key / value types. */
   {
-    hb_hashmap_t<hb_bytes_t, int, std::nullptr_t, int, nullptr, 0> m1;
-    hb_hashmap_t<int, hb_bytes_t, int, std::nullptr_t, 0, nullptr> m2;
-    hb_hashmap_t<hb_bytes_t, hb_bytes_t, std::nullptr_t, std::nullptr_t, nullptr, nullptr> m3;
+    hb_hashmap_t<hb_bytes_t, int> m1;
+    hb_hashmap_t<int, hb_bytes_t> m2;
+    hb_hashmap_t<hb_bytes_t, hb_bytes_t> m3;
     assert (m1.get_population () == 0);
     assert (m2.get_population () == 0);
     assert (m3.get_population () == 0);
   }
 
   {
-    hb_hashmap_t<int, int, int, int, 0, 0> m0;
-    hb_hashmap_t<std::string, int, const std::string*, int, &invalid, 0> m1;
-    hb_hashmap_t<int, std::string, int, const std::string*, 0, &invalid> m2;
-    hb_hashmap_t<std::string, std::string, const std::string*, const std::string*, &invalid, &invalid> m3;
+    hb_hashmap_t<int, int> m0;
+    hb_hashmap_t<std::string, int> m1;
+    hb_hashmap_t<int, std::string> m2;
+    hb_hashmap_t<std::string, std::string> m3;
 
     std::string s;
     for (unsigned i = 1; i < 1000; i++)
@@ -135,5 +147,64 @@ main (int argc, char **argv)
     }
   }
 
+  /* Test hashing maps. */
+  {
+    using pair = hb_pair_t<hb_codepoint_t, hb_codepoint_t>;
+
+    hb_hashmap_t<hb_map_t, hb_map_t> m1;
+
+    m1.set (hb_map_t (), hb_map_t {});
+    m1.set (hb_map_t (), hb_map_t {pair (1u, 2u)});
+    m1.set (hb_map_t {pair (1u, 2u)}, hb_map_t {pair (2u, 3u)});
+
+    assert (m1.get (hb_map_t ()) == hb_map_t {pair (1u, 2u)});
+    assert (m1.get (hb_map_t {pair (1u, 2u)}) == hb_map_t {pair (2u, 3u)});
+  }
+
+  /* Test hashing sets. */
+  {
+    hb_hashmap_t<hb_set_t, hb_set_t> m1;
+
+    m1.set (hb_set_t (), hb_set_t ());
+    m1.set (hb_set_t (), hb_set_t {1});
+    m1.set (hb_set_t {1, 1000}, hb_set_t {2});
+
+    assert (m1.get (hb_set_t ()) == hb_set_t {1});
+    assert (m1.get (hb_set_t {1000, 1}) == hb_set_t {2});
+  }
+
+  /* Test hashing vectors. */
+  {
+    using vector_t = hb_vector_t<unsigned>;
+
+    hb_hashmap_t<vector_t, vector_t> m1;
+
+    m1.set (vector_t (), vector_t ());
+    m1.set (vector_t (), vector_t {1});
+    m1.set (vector_t {1}, vector_t {2});
+
+    assert (m1.get (vector_t ()) == vector_t {1});
+    assert (m1.get (vector_t {1}) == vector_t {2});
+  }
+
+  /* Test hb::shared_ptr. */
+  hb_hash (hb::shared_ptr<hb_set_t> ());
+  {
+    hb_hashmap_t<hb::shared_ptr<hb_set_t>, hb::shared_ptr<hb_set_t>> m;
+
+    m.get (hb::shared_ptr<hb_set_t> ());
+    m.get (hb::shared_ptr<hb_set_t> (hb_set_get_empty ()));
+    m.iter ();
+  }
+  /* Test hb::unique_ptr. */
+  hb_hash (hb::unique_ptr<hb_set_t> ());
+  {
+    hb_hashmap_t<hb::unique_ptr<hb_set_t>, hb::unique_ptr<hb_set_t>> m;
+
+    m.get (hb::unique_ptr<hb_set_t> ());
+    m.get (hb::unique_ptr<hb_set_t> (hb_set_get_empty ()));
+    m.iter_ref ();
+  }
+
   return 0;
 }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc b/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc
index fab63acb63b60d5a0dcb329fecd25ec86d8fdf49..e83d72c7ea1d4ff3088ecc04262e1296e7eb5ab0 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc
@@ -73,17 +73,9 @@ test_extract ()
   assert (queue.is_empty ());
 }
 
-static void
-test_extract_empty ()
-{
-  hb_priority_queue_t queue;
-  assert (queue.pop_minimum () == hb_pair (0, 0));
-}
-
 int
 main (int argc, char **argv)
 {
   test_insert ();
   test_extract ();
-  test_extract_empty ();
 }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc b/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc
index 3a2536a29a4b0b610cba1d7bb62a91e8b17802e9..69863b562f250b706bf46d46c3e4533a3a736a25 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc
@@ -28,6 +28,7 @@
 
 #include "hb-repacker.hh"
 #include "hb-open-type.hh"
+#include "graph/serialize.hh"
 
 static void start_object(const char* tag,
                          unsigned len,
@@ -728,26 +729,6 @@ populate_serializer_with_split_spaces_expected_2 (hb_serialize_context_t* c)
   c->end_serialize();
 }
 
-static void
-populate_serializer_complex_1 (hb_serialize_context_t* c)
-{
-  c->start_serialize<char> ();
-
-  unsigned obj_4 = add_object ("jkl", 3, c);
-  unsigned obj_3 = add_object ("ghi", 3, c);
-
-  start_object ("def", 3, c);
-  add_offset (obj_3, c);
-  unsigned obj_2 = c->pop_pack (false);
-
-  start_object ("abc", 3, c);
-  add_offset (obj_2, c);
-  add_offset (obj_4, c);
-  c->pop_pack (false);
-
-  c->end_serialize();
-}
-
 static void
 populate_serializer_complex_2 (hb_serialize_context_t* c)
 {
@@ -831,68 +812,6 @@ populate_serializer_virtual_link (hb_serialize_context_t* c)
   c->end_serialize();
 }
 
-static void test_sort_kahn_1 ()
-{
-  size_t buffer_size = 100;
-  void* buffer = malloc (buffer_size);
-  hb_serialize_context_t c (buffer, buffer_size);
-  populate_serializer_complex_1 (&c);
-
-  graph_t graph (c.object_graph ());
-  graph.sort_kahn ();
-
-  assert(strncmp (graph.object (3).head, "abc", 3) == 0);
-  assert(graph.object (3).real_links.length == 2);
-  assert(graph.object (3).real_links[0].objidx == 2);
-  assert(graph.object (3).real_links[1].objidx == 1);
-
-  assert(strncmp (graph.object (2).head, "def", 3) == 0);
-  assert(graph.object (2).real_links.length == 1);
-  assert(graph.object (2).real_links[0].objidx == 0);
-
-  assert(strncmp (graph.object (1).head, "jkl", 3) == 0);
-  assert(graph.object (1).real_links.length == 0);
-
-  assert(strncmp (graph.object (0).head, "ghi", 3) == 0);
-  assert(graph.object (0).real_links.length == 0);
-
-  free (buffer);
-}
-
-static void test_sort_kahn_2 ()
-{
-  size_t buffer_size = 100;
-  void* buffer = malloc (buffer_size);
-  hb_serialize_context_t c (buffer, buffer_size);
-  populate_serializer_complex_2 (&c);
-
-  graph_t graph (c.object_graph ());
-  graph.sort_kahn ();
-
-
-  assert(strncmp (graph.object (4).head, "abc", 3) == 0);
-  assert(graph.object (4).real_links.length == 3);
-  assert(graph.object (4).real_links[0].objidx == 3);
-    assert(graph.object (4).real_links[1].objidx == 0);
-  assert(graph.object (4).real_links[2].objidx == 2);
-
-  assert(strncmp (graph.object (3).head, "def", 3) == 0);
-  assert(graph.object (3).real_links.length == 1);
-  assert(graph.object (3).real_links[0].objidx == 1);
-
-  assert(strncmp (graph.object (2).head, "mn", 2) == 0);
-  assert(graph.object (2).real_links.length == 0);
-
-  assert(strncmp (graph.object (1).head, "ghi", 3) == 0);
-  assert(graph.object (1).real_links.length == 1);
-  assert(graph.object (1).real_links[0].objidx == 0);
-
-  assert(strncmp (graph.object (0).head, "jkl", 3) == 0);
-  assert(graph.object (0).real_links.length == 0);
-
-  free (buffer);
-}
-
 static void test_sort_shortest ()
 {
   size_t buffer_size = 100;
@@ -1013,7 +932,7 @@ test_serialize ()
   hb_bytes_t expected = c1.copy_bytes ();
 
   graph_t graph (c1.object_graph ());
-  hb_blob_t* out = graph.serialize ();
+  hb_blob_t* out = graph::serialize (graph);
   free (buffer_1);
 
   hb_bytes_t actual = out->as_bytes ();
@@ -1030,7 +949,7 @@ static void test_will_overflow_1 ()
   populate_serializer_complex_2 (&c);
   graph_t graph (c.object_graph ());
 
-  assert (!graph.will_overflow (nullptr));
+  assert (!graph::will_overflow (graph, nullptr));
 
   free (buffer);
 }
@@ -1043,7 +962,7 @@ static void test_will_overflow_2 ()
   populate_serializer_with_overflow (&c);
   graph_t graph (c.object_graph ());
 
-  assert (graph.will_overflow (nullptr));
+  assert (graph::will_overflow (graph, nullptr));
 
   free (buffer);
 }
@@ -1056,7 +975,7 @@ static void test_will_overflow_3 ()
   populate_serializer_with_dedup_overflow (&c);
   graph_t graph (c.object_graph ());
 
-  assert (graph.will_overflow (nullptr));
+  assert (graph::will_overflow (graph, nullptr));
 
   free (buffer);
 }
@@ -1336,8 +1255,6 @@ int
 main (int argc, char **argv)
 {
   test_serialize ();
-  test_sort_kahn_1 ();
-  test_sort_kahn_2 ();
   test_sort_shortest ();
   test_will_overflow_1 ();
   test_will_overflow_2 ();
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc b/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc
index 286f1a9976cef6a915a4a2ce5b62d9a55c3a2d86..983a15910dff5f8e910242740a42acc440e80ef2 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc
@@ -20,13 +20,11 @@
  * 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"
 #include "hb-set.hh"
 
-
 int
 main (int argc, char **argv)
 {
@@ -42,21 +40,26 @@ main (int argc, char **argv)
   /* Test copy assignment. */
   {
     hb_set_t v1 {1, 2};
-    hb_set_t v2 = v1;
+    hb_set_t v2;
+    v2 = v1;
     assert (v1.get_population () == 2);
     assert (v2.get_population () == 2);
   }
 
   /* Test move constructor. */
   {
-    hb_set_t v {hb_set_t {1, 2}};
+    hb_set_t s {1, 2};
+    hb_set_t v (std::move (s));
+    assert (s.get_population () == 0);
     assert (v.get_population () == 2);
   }
 
   /* Test move assignment. */
   {
+    hb_set_t s = hb_set_t {1, 2};
     hb_set_t v;
-    v = hb_set_t {1, 2};
+    v = std::move (s);
+    assert (s.get_population () == 0);
     assert (v.get_population () == 2);
   }
 
@@ -67,9 +70,15 @@ main (int argc, char **argv)
     s.add (18);
     s.add (12);
 
-    hb_set_t v (s);
+    hb_vector_t<hb_codepoint_t> v (s);
+    hb_set_t v0 (v);
+    hb_set_t v1 (s);
+    hb_set_t v2 (std::move (s));
 
-    assert (v.get_population () == 2);
+    assert (s.get_population () == 0);
+    assert (v0.get_population () == 2);
+    assert (v1.get_population () == 2);
+    assert (v2.get_population () == 2);
   }
 
   /* Test initializing from iterator. */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc b/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc
index 521f2a171d328d2961d4af4171e65086489c4a9c..37bec309d0f6053f338fe6697af15c138d33f2b9 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc
@@ -61,8 +61,12 @@ main (int argc, char **argv)
 
   /* Test move constructor. */
   {
-    hb_vector_t<int> v {hb_vector_t<int> {1, 2}};
-    hb_vector_t<int> V {hb_vector_t<int> {1, 2}};
+    hb_vector_t<int> s {1, 2};
+    hb_sorted_vector_t<int> S {1, 2};
+    hb_vector_t<int> v (std::move (s));
+    hb_sorted_vector_t<int> V (std::move (S));
+    assert (s.length == 0);
+    assert (S.length == 0);
     assert (v.length == 2);
     assert (v[0] == 1);
     assert (v[1] == 2);
@@ -70,11 +74,16 @@ main (int argc, char **argv)
 
   /* Test move assignment. */
   {
+    hb_vector_t<int> s {1, 2};
+    hb_sorted_vector_t<int> S {1, 2};
     hb_vector_t<int> v;
     hb_sorted_vector_t<int> V;
-    v = hb_vector_t<int> {1, 2};
-    V = hb_sorted_vector_t<int> {1, 2};
+    v = std::move (s);
+    V = std::move (S);
+    assert (s.length == 0);
+    assert (S.length == 0);
     assert (v.length == 2);
+    assert (V.length == 2);
     assert (v[0] == 1);
     assert (v[1] == 2);
   }
@@ -137,7 +146,6 @@ main (int argc, char **argv)
     assert (v2[2] == 3);
   }
 
-#if 0
   {
     hb_vector_t<std::string> v;
 
@@ -147,8 +155,13 @@ main (int argc, char **argv)
       s += "x";
       v.push (s);
     }
+
+    hb_vector_t<std::string> v2;
+
+    v2 = v;
+
+    v2.remove (50);
   }
-#endif
 
   return 0;
 }
diff --git a/source/libs/harfbuzz/version.ac b/source/libs/harfbuzz/version.ac
index ef40866be783410d039f587cac8c4cb9b1121a8a..57cb3c7fdf8bd4a98f33c663b83b43de1af2a203 100644
--- a/source/libs/harfbuzz/version.ac
+++ b/source/libs/harfbuzz/version.ac
@@ -8,4 +8,4 @@ dnl
 dnl --------------------------------------------------------
 dnl
 dnl  m4-include this file to define the current harfbuzz version
-m4_define([harfbuzz_version], [4.2.1])
+m4_define([harfbuzz_version], [4.4.1])
diff --git a/source/texk/README b/source/texk/README
index 9b33c1372d867603a2445dfa4b4a32e0a2d7a8ba..4d7564734ac1ecdc5c8953fc340c672223a3ec73 100644
--- a/source/texk/README
+++ b/source/texk/README
@@ -1,4 +1,4 @@
-$Id: README 62463 2022-03-06 01:07:46Z hironobu $
+$Id: README 63453 2022-05-31 15:40:57Z karl $
 Copyright 2006-2022 TeX Users Group.
 You may freely use, modify and/or distribute this file.
 
@@ -23,8 +23,7 @@ afm2pl - maintained here
   old info: http://tex.aanhet.net/afm2pl/
 
 bibtex-x - maintained here, contains
-  bibtex8
-  bibtexu
+  bibtex8, bibtexu
     old info: http://omega.enstb.org/yannis/bibtexu/
 
 chktex 1.7.6 - checked 15mar20
@@ -41,7 +40,7 @@ detex 2.8.9 - checked 4mar21
 
 dtl - maintained here, by Takuji
 
-dvi2tty 6.0.0 - checked 15mar20
+dvi2tty 6.0.2 - checked 1may22
   https://github.com/t-tk/dvi2tty/releases
 
 dvidvi - maintained here
@@ -53,12 +52,11 @@ dviout-util - by Japanese TeX Development Community (Hironobu, Takuji et al.)
   but also: https://github.com/aminophen/dviout-util
 
 dvipdfm-x - maintained here, by us, contains
-  dvipdfmx
-  xdvipdfmx
+  dvipdfmx and xdvipdfmx.
     old info: http://project.ktug.org/dvipdfmx/
 
-dvipng 1.17 - checked 06jan20
-  http://mirror.ctan.org/dviware/dvipng/
+dvipng 1.17 - checked 31may22
+  https://mirror.ctan.org/dviware/dvipng/
 
 dvipos - maintained here, by us
 
@@ -70,14 +68,14 @@ dvisvgm 2.13.3 - checked 28feb22
   https://ctan.org/pkg/dvisvgm
 
 gregorio 6.0.0 - checked 14mar21
-  CTAN/support/gregoriotex/gregorio-6.0.0.zip
+  https://mirror.ctan.org/support/gregoriotex/gregorio-6.0.0.zip
 
 gsftopk - from Paul Vojta's xdvi.
 
 kpathsea - maintained here, by us
 
-lcdf-typetools 2.108 - checked 15mar20
-  http://www.lcdf.org/type/
+lcdf-typetools 2.108 - checked 31may22
+  https://www.lcdf.org/type/
 
 makeindexk - maintained here, by us
 
@@ -109,8 +107,8 @@ ttf2pk2 - maintained here, by us
 
 ttfdump - maintained here, by us, since Taiwan upstream apparently gone.
 
-upmendex 1.00 - by Takuji Tanaka
-  http://www.ctan.org/pkg/upmendex
+upmendex 1.01 - by Takuji Tanaka
+  https://ctan.org/pkg/upmendex
   https://github.com/t-tk/upmendex-package
 
 web2c - maintained here, by us - core web2c, plain tex, etc.
@@ -139,8 +137,8 @@ web2c - maintained here, by us - core web2c, plain tex, etc.
        uptex - http://www.t-lab.opal.ne.jp/tex/uptex_en.html
        xetex - http://tug.org/xetex/
    
-xdvik 22.87.06 - checked 18feb22
-  http://sourceforge.net/projects/xdvi/files/xdvik/
+xdvik 22.87.06 - checked 31may22
+  https://sourceforge.net/projects/xdvi/files/xdvik/
   Procedure for updating xdvik from sourceforge release:
 tar xf ...
 diff -crN2 \
diff --git a/source/texk/kpathsea/ChangeLog b/source/texk/kpathsea/ChangeLog
index f1d65b75d87c9b19dbbb93817b797ab26040e94f..5c6dccf80fc903c6058429417e260994fb8fb6ad 100644
--- a/source/texk/kpathsea/ChangeLog
+++ b/source/texk/kpathsea/ChangeLog
@@ -1,3 +1,17 @@
+2022-06-12  TANAKA Takuji  <ttk@t-lab.opal.ne.jp>
+
+	* texmf.cnf (guess_input_kanji_encoding):
+	Support guessing input file encodings for unix-like platforms.
+	(It has already been supported on Windows.)
+	Now we set default on for (e)p(la)tex, pbibtex & mendex.
+	https://github.com/texjporg/tex-jp-build/issues/142
+
+2022-05-06  TANAKA Takuji  <ttk@t-lab.opal.ne.jp>
+
+	* texmf.cnf (max_print_line):
+	Now it applies to BibTeX family:
+	bibtex, pbibtex, upbibtex, bibtex8 and bibtexu.
+
 2022-03-21  Karl Berry  <karl@tug.org>
 
 	* TL'22 release.
diff --git a/source/texk/kpathsea/texmf.cnf b/source/texk/kpathsea/texmf.cnf
index b5d2dfda66833f09841377e97223ea2acc203d38..8cc17505443c1ef7652fc98abd79bd4f4bc8ba97 100644
--- a/source/texk/kpathsea/texmf.cnf
+++ b/source/texk/kpathsea/texmf.cnf
@@ -864,6 +864,7 @@ gf_buf_size = 16384  % MF
 % 30 < half_error_line < error_line - 15;
 % 60 <= max_print_line;
 % These apply to TeX, Metafont, and MetaPost.
+% "max_print_line" applies to BibTeX family.
 error_line = 79
 half_error_line = 50
 max_print_line = 79
@@ -887,8 +888,14 @@ max_rows.gftype = 8191
 max_cols.gftype = 8191
 
 % Guess input encoding (SJIS vs. Unicode, etc.) in pTeX and friends?
-% Default is 1, to guess. Used on Windows only.
-guess_input_kanji_encoding = 1
+% Default is 1 for (e)p(la)tex, pbibtex & mendex, to guess.
+guess_input_kanji_encoding = 0
+guess_input_kanji_encoding.ptex = 1
+guess_input_kanji_encoding.eptex = 1
+guess_input_kanji_encoding.platex = 1
+guess_input_kanji_encoding.platex-dev = 1
+guess_input_kanji_encoding.pbibtex = 1
+guess_input_kanji_encoding.mendex = 1
 
 % command_line_encoding
 %
@@ -901,6 +908,7 @@ guess_input_kanji_encoding = 1
 % ignored. If file names in sources and DVI or XDV files are ASCII
 % only, the value of command_line_encoding is irrelevant.
 % If command_line_encoding = utf8 or command_line_encoding = utf-8,
+% characters on a command line in cmd.exe are assumed to be Unicode and
 % file names in sources and DVI or XDV files are assumed to be UTF-8.
 % Default is utf-8, from 2019-07-24, to assume UTF-8 file names.
 % In order to switch off the function, define an environment variable:
diff --git a/source/texk/web2c/ChangeLog b/source/texk/web2c/ChangeLog
index 952b7b631f8c2e795d9c33602ef8c6f4b52706d3..a876a06accefb7836639872671017cbe42a7126c 100644
--- a/source/texk/web2c/ChangeLog
+++ b/source/texk/web2c/ChangeLog
@@ -1,3 +1,41 @@
+2022-07-16  Karl Berry  <karl@freefriends.org>
+
+	* tex.ch (25.369, <Suppress expansion ...>): disallow
+	\noexpand\endwrite, else we get a seg fault because of fetching
+	mem[null]. Fix from drf.
+	Original report: https://tex.stackexchange.com/questions/609423
+	TeX bug entry: https://tug.org/texmfbug/newbug.html#B155endwrite
+
+2022-06-12  TANAKA Takuji  <ttk@t-lab.opal.ne.jp>
+
+	* texmfmp-help.h ((e)(u)pTeX), help.h ((u)pBibTeX):
+	Support guessing input file encodings.
+	https://github.com/texjporg/tex-jp-build/issues/142
+	* tests/enc-{asc,jis,sjis,euc,utf8{,a,b},amb[012]}.bib,
+	tests/enc{,-[es]}.aux, tests/testfield.bst,
+	tests/enc-{,[es]}[pu].bbl:
+	Add tests for guess encodings & (u)pBibTeX.
+
+2022-06-05  Andreas Scherer  <https://ascherer.github.io>
+
+	* ctangleboot.cin: CWEB 4.8 release.
+
+2022-05-06  TANAKA Takuji  <ttk@t-lab.opal.ne.jp>
+
+	* bibtex.ch:
+	Apply max_print_line variable in kpathsea to ((u)p)BibTeX.
+	* bibtex.test, tests/exampl.bbl, am/web.am:
+	Update tests for ((u)p)BibTeX.
+
+2022-04-30  Andreas Scherer  <https://ascherer.github.io>
+
+	* weav-twill.ch,
+	* weave.ch: Reshuffle '\title' changes..
+
+2022-04-27  Andreas Scherer  <https://ascherer.github.io>
+
+	* weav-twill.ch: Avoid non-POSIX conflict with Solaris 11.4.
+
 2022-04-06  Andreas Scherer  <https://ascherer.github.io>
 
 	* weav-twill.ch: My name is 'twill'.
diff --git a/source/texk/web2c/Makefile.in b/source/texk/web2c/Makefile.in
index ae75d9991b5b4c0643c2fd7c2d0e274cbabfb55b..be85efcc4bfb3df170e6f511637d76f3c18a3995 100644
--- a/source/texk/web2c/Makefile.in
+++ b/source/texk/web2c/Makefile.in
@@ -3205,19 +3205,19 @@ TEST_EXTENSIONS = .pl .test
 EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \
 	triptrap-sh tangle.web tangle.ch tangleboot.pin cwebdir \
 	ctangleboot.cin cwebboot.cin tangle.test $(web_programs:=.web) \
-	$(web_programs:=.ch) $(web_tests) tests/memdata1.bst \
-	tests/memdata2.bst tests/memdata3.bst tests/bibtex-bigauth.aux \
-	tests/bibtex-bigauth.bib tests/bibtex-bigauth.tex \
-	tests/auxinclude.aux tests/auxinclude.bbl tests/auxinclude.bib \
-	tests/auxinclude.tex tests/auxinclude2.aux \
-	tests/auxinclude2.tex tests/allbib.aux tests/allbib.tex \
-	tests/apalike.bst tests/badpl.pl tests/badtfm.tfm \
-	tests/badvf.tfm tests/badvf.vf tests/badvpl.vpl \
-	tests/batch.tex tests/check.log tests/cmr10.600gf \
-	tests/cmr10.pk tests/cmr10.pl tests/cmr10.tfm tests/cmr7.tfm \
-	tests/cmr8.tfm tests/cmti10.tfm tests/cmtt10.tfm \
-	tests/cmtt8.tfm tests/dict tests/eight.tex tests/end.tex \
-	tests/exampl.aux tests/exampl.tex tests/gray.tfm \
+	$(web_programs:=.ch) $(web_tests) tests/exampl.bbl \
+	tests/memdata1.bst tests/memdata2.bst tests/memdata3.bst \
+	tests/bibtex-bigauth.aux tests/bibtex-bigauth.bib \
+	tests/bibtex-bigauth.tex tests/auxinclude.aux \
+	tests/auxinclude.bbl tests/auxinclude.bib tests/auxinclude.tex \
+	tests/auxinclude2.aux tests/auxinclude2.tex tests/allbib.aux \
+	tests/allbib.tex tests/apalike.bst tests/badpl.pl \
+	tests/badtfm.tfm tests/badvf.tfm tests/badvf.vf \
+	tests/badvpl.vpl tests/batch.tex tests/check.log \
+	tests/cmr10.600gf tests/cmr10.pk tests/cmr10.pl \
+	tests/cmr10.tfm tests/cmr7.tfm tests/cmr8.tfm tests/cmti10.tfm \
+	tests/cmtt10.tfm tests/cmtt8.tfm tests/dict tests/eight.tex \
+	tests/end.tex tests/exampl.aux tests/exampl.tex tests/gray.tfm \
 	tests/hello.tex tests/io.mf tests/just.texi tests/label.mp \
 	tests/label.mpx tests/logo8.tfm tests/longline.aux \
 	tests/longline.bib tests/longline.tex tests/longtfm.tex \
@@ -3282,18 +3282,23 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \
 	ptexdir/Changes.txt ptexdir/INSTALL.txt ptexdir/README.txt \
 	$(ptex_tests) $(pweb_tests) ptexdir/tests/nissya_bib.aux \
 	ptexdir/tests/nissya.bst ptexdir/tests/sample.bib \
-	tests/memdata1.bst tests/memdata2.bst tests/memdata3.bst \
-	ptexdir/tests/goth10.tfm ptexdir/tests/sample.dvi \
-	ptexdir/tests/samplea.typ ptexdir/tests/min10.pl \
-	ptexdir/tests/min10.tfm ptexdir/tests/tmin10.pl \
-	ptexdir/tests/tmin10.tfm ptexdir/tests/skipjfmp.pl \
-	ptexdir/tests/skipjfmp.tfm ptexdir/ptrip/ptrip.diffs \
-	ptexdir/ptrip/texmf.cnf $(eptex_web_srcs) $(eptex_ch_srcs) \
-	eptexdir/eptex.defines eptexdir/ChangeLog eptexdir/Changes.txt \
-	eptexdir/README.txt $(eptex_tests) \
-	eptexdir/eptrip/eptrip.diffs eptexdir/eptrip/eptrip.log \
-	eptexdir/eptrip/eptrip.tex eptexdir/eptrip/texmf.cnf \
-	eptexdir/pdfprimitive.test \
+	tests/testfield.bst tests/enc-asc.bib tests/enc-jis.bib \
+	tests/enc-sjis.bib tests/enc-euc.bib tests/enc-utf8.bib \
+	tests/enc-utf8a.bib tests/enc-utf8b.bib tests/enc-amb0.bib \
+	tests/enc-amb1.bib tests/enc-amb2.bib tests/enc.aux \
+	tests/enc-e.aux tests/enc-s.aux tests/enc-p.bbl \
+	tests/enc-ep.bbl tests/enc-sp.bbl tests/memdata1.bst \
+	tests/memdata2.bst tests/memdata3.bst ptexdir/tests/goth10.tfm \
+	ptexdir/tests/sample.dvi ptexdir/tests/samplea.typ \
+	ptexdir/tests/min10.pl ptexdir/tests/min10.tfm \
+	ptexdir/tests/tmin10.pl ptexdir/tests/tmin10.tfm \
+	ptexdir/tests/skipjfmp.pl ptexdir/tests/skipjfmp.tfm \
+	ptexdir/ptrip/ptrip.diffs ptexdir/ptrip/texmf.cnf \
+	$(eptex_web_srcs) $(eptex_ch_srcs) eptexdir/eptex.defines \
+	eptexdir/ChangeLog eptexdir/Changes.txt eptexdir/README.txt \
+	$(eptex_tests) eptexdir/eptrip/eptrip.diffs \
+	eptexdir/eptrip/eptrip.log eptexdir/eptrip/eptrip.tex \
+	eptexdir/eptrip/texmf.cnf eptexdir/pdfprimitive.test \
 	eptexdir/tests/pdfprimitive-test.tex \
 	eptexdir/tests/pdfprimitive-eptex.log $(uptex_web_srcs) \
 	$(uptex_ch_srcs) uptexdir/uptex.defines ptexdir/pbibtex.ch \
@@ -3301,6 +3306,7 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \
 	ptexdir/ppltotf.ch uptexdir/uppltotf.ch ptexdir/ptftopl.ch \
 	uptexdir/uptftopl.ch uptexdir/COPYRIGHT uptexdir/COPYRIGHT.jis \
 	uptexdir/ChangeLog $(uptex_tests) $(upweb_tests) \
+	tests/enc-u.bbl tests/enc-eu.bbl tests/enc-su.bbl \
 	uptexdir/tests/umin10.pl uptexdir/tests/umin10.tfm \
 	uptexdir/tests/utmin10.pl uptexdir/tests/utmin10.tfm \
 	uptexdir/tests/testnewu.pl uptexdir/tests/testnewu.tfm \
@@ -3623,30 +3629,30 @@ DISTCLEANFILES = CXXLD.sh tangle.c tangle.h tangle.p tangle-web2c \
 	$(pweb_programs:=.h) $(pweb_programs:=.p) \
 	$(pweb_programs:=-web2c) $(pweb_programs:=.web) \
 	ptests/nissya_bib.* ptests/xexampl.aux ptests/xexampl.bbl \
-	ptests/xexampl.blg ptests/memtest.bib ptests/memtest?.* \
-	ptests/xstory.dvityp ptests/xpagenum.typ ptests/xcmr10.tfm \
-	ptests/xcmr10.pl ptests/xsample.typ ptests/x*min10.* \
-	ptests/xskipjfmp.* ptrip.diffs $(nodist_eptex_SOURCES) \
-	eptex.web eptex.ch eptex-web2c eptex.p eptex.pool eptex-tangle \
-	eptrip.diffs pdfprimitive-eptex.* $(nodist_uptex_SOURCES) \
-	uptex.web uptex.ch uptex-web2c uptex.p uptex.pool uptex-tangle \
-	$(upweb_programs:=.c) $(upweb_programs:=.h) \
-	$(upweb_programs:=.p) $(upweb_programs:=-web2c) \
-	$(upweb_programs:=.web) uptests/xexampl.aux \
-	uptests/xexampl.bbl uptests/xexampl.blg uptests/xstory.dvityp \
-	uptests/xpagenum.typ uptests/xcmr10.tfm uptests/xcmr10.pl \
-	uptests/x*min10.* uptests/xtestnewu.* uptests/xuparse.* \
-	uptests/yuparse.* uptests/ygkhuge*.* uptrip.diffs \
-	$(nodist_euptex_SOURCES) euptex.web euptex.ch euptex-web2c \
-	euptex.p euptex.pool euptex-tangle euptrip.diffs \
-	pdfprimitive-euptex.* $(nodist_hitex_SOURCES) \
-	$(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) \
-	hiformat-tangle hitex-tangle hello.log rule.log \
-	$(nodist_pdftex_SOURCES) pdftex-final.ch pdftex-web2c pdftex.p \
-	pdftex.pool pdftex-tangle pwprob.log pwprob.tex pdfimage.fmt \
-	pdfimage.log pdfimage.pdf expanded.log cnfline.log \
-	partoken-ok.log partoken-xfail.log postV3.afm postV7.afm \
-	test-13.pdf test-13.xref test-15.pdf test-15.xref \
+	ptests/xexampl.blg ptests/xenc*.* ptests/memtest.bib \
+	ptests/memtest?.* ptests/xstory.dvityp ptests/xpagenum.typ \
+	ptests/xcmr10.tfm ptests/xcmr10.pl ptests/xsample.typ \
+	ptests/x*min10.* ptests/xskipjfmp.* ptrip.diffs \
+	$(nodist_eptex_SOURCES) eptex.web eptex.ch eptex-web2c eptex.p \
+	eptex.pool eptex-tangle eptrip.diffs pdfprimitive-eptex.* \
+	$(nodist_uptex_SOURCES) uptex.web uptex.ch uptex-web2c uptex.p \
+	uptex.pool uptex-tangle $(upweb_programs:=.c) \
+	$(upweb_programs:=.h) $(upweb_programs:=.p) \
+	$(upweb_programs:=-web2c) $(upweb_programs:=.web) \
+	uptests/xexampl.aux uptests/xexampl.bbl uptests/xexampl.blg \
+	uptests/xenc*.* uptests/xstory.dvityp uptests/xpagenum.typ \
+	uptests/xcmr10.tfm uptests/xcmr10.pl uptests/x*min10.* \
+	uptests/xtestnewu.* uptests/xuparse.* uptests/yuparse.* \
+	uptests/ygkhuge*.* uptrip.diffs $(nodist_euptex_SOURCES) \
+	euptex.web euptex.ch euptex-web2c euptex.p euptex.pool \
+	euptex-tangle euptrip.diffs pdfprimitive-euptex.* \
+	$(nodist_hitex_SOURCES) $(nodist_hishrink_SOURCES) \
+	$(nodist_histretch_SOURCES) hiformat-tangle hitex-tangle \
+	hello.log rule.log $(nodist_pdftex_SOURCES) pdftex-final.ch \
+	pdftex-web2c pdftex.p pdftex.pool pdftex-tangle pwprob.log \
+	pwprob.tex pdfimage.fmt pdfimage.log pdfimage.pdf expanded.log \
+	cnfline.log partoken-ok.log partoken-xfail.log postV3.afm \
+	postV7.afm test-13.pdf test-13.xref test-15.pdf test-15.xref \
 	$(nodist_libluatex_sources) luaimage.* luajitimage.* \
 	$(nodist_xetex_SOURCES) xetex.web xetex-final.ch xetex-web2c \
 	xetex.p xetex.pool xetex-tangle bug73.fmt bug73.log bug73.out \
diff --git a/source/texk/web2c/README b/source/texk/web2c/README
index 780834affc075334b689745e262d9d45ebc4da67..50911f14e02cc599c8701b523ab70876d23af7fb 100644
--- a/source/texk/web2c/README
+++ b/source/texk/web2c/README
@@ -1,4 +1,4 @@
-$Id: README 59252 2021-05-18 16:58:12Z karl $
+$Id: README 63453 2022-05-31 15:40:57Z karl $
 (This file is public domain.)
 
 This directory contains Web2c, a system which converts TeX, Metafont,
@@ -9,10 +9,20 @@ or WEB-to-C translator.
 Web2c no longer exists as a separate distribution.  It is maintained as
 part of TeX Live (http://tug.org/texlive).
 
-See `NEWS' for changes by release,
-`ChangeLog` for all changes.
-`PROJECTS' for some old rainy-day ideas.
+Mailing list for bug reports and all general discussion:
+https://lists.tug.org/tex-k; anyone can join the list, but it is not
+necessary to join to post. Archives are public.
 
+See `NEWS' for changes by release, `ChangeLog` for all changes,
+`PROJECTS' for some old rainy-day ideas.
 If present, see *dir/README for more details on each package.
 
+Info on building only one of the engines here:
+  https://tug.org/texinfohtml/tlbuild.html#Build-one-engine
+TeX Live general build info:
+  https://tug.org/texlive/build.html
+Mailing list for build issues (same setup as tex-k, above):
+  https://lists.tug.org/tlbuild
+
+The original TeX and Metafont programs by Donald Knuth are public domain.
 Web2c is free software.
diff --git a/source/texk/web2c/am/web.am b/source/texk/web2c/am/web.am
index a1dd0c2e351386c032e21fa29e15ecc269f6d10f..dfea262c7f2d2084c50b60b30b7a9fb7fb9bb575 100644
--- a/source/texk/web2c/am/web.am
+++ b/source/texk/web2c/am/web.am
@@ -1,4 +1,4 @@
-## $Id: web.am 61575 2022-01-11 22:47:10Z karl $
+## $Id: web.am 63239 2022-05-06 08:08:17Z takuji $
 ## texk/web2c/am/web.am: Makefile fragment for the standard web programs --
 ## except tangle.
 ##
@@ -194,6 +194,7 @@ endif WEB
 EXTRA_DIST += $(web_tests)
 
 ## bibtex.test
+EXTRA_DIST += tests/exampl.bbl
 DISTCLEANFILES += tests/xexampl.aux tests/xexampl.bbl tests/xexampl.blg
 ## tests/bibtex-longline-test.pl
 DISTCLEANFILES += longline.aux longline.bbl longline.bib longline.blg
diff --git a/source/texk/web2c/bibtex.ch b/source/texk/web2c/bibtex.ch
index 6e04e796d385d081c22491cef4609b9c3e520e9f..f8ab25b7eed9354a32cbbc00aa7ba84a4f7e4c5f 100644
--- a/source/texk/web2c/bibtex.ch
+++ b/source/texk/web2c/bibtex.ch
@@ -199,9 +199,13 @@ end.
 
 @x [still 14]
 @!buf_size=1000; {maximum number of characters in an input line (or string)}
+@!min_print_line=3; {minimum \.{.bbl} line length: must be |>=3|}
+@!max_print_line=79; {the maximum: must be |>min_print_line| and |<buf_size|}
 @y
 @!BUF_SIZE=20000; {initial maximum number of characters in an input line
                                                                 (or string)}
+@!min_print_line=3; {minimum \.{.bbl} line length: must be |>=3|}
+@!MAX_PRINT_LINE=60; {the maximum: must be |>min_print_line| and |<buf_size|}
 @z
 
 @x [still 14]
@@ -278,6 +282,7 @@ end.
 @y
 @<Globals in the outer block@>=
 @!pool_size: integer;
+@!max_print_line: integer;
 @!max_bib_files: integer;
 @!max_cites: integer;
 @!wiz_fn_space: integer;
@@ -1747,6 +1752,7 @@ begin kpse_set_program_name (argv[0], 'bibtex');
 setup_bound_var (ENT_STR_SIZE)('ent_str_size')(ent_str_size);
 setup_bound_var (GLOB_STR_SIZE)('glob_str_size')(glob_str_size);
 setup_bound_var (MAX_STRINGS)('max_strings')(max_strings);
+setup_bound_var (MAX_PRINT_LINE)('max_print_line')(max_print_line);
 @#
 hash_size := max_strings;
 if hash_size < HASH_SIZE then hash_size := HASH_SIZE;
diff --git a/source/texk/web2c/bibtex.test b/source/texk/web2c/bibtex.test
index 011f2b946936a6ac064886fcdc3abd644fbeb371..bda99913d5d63ec3032c9fc206521be88c41fe7a 100644
--- a/source/texk/web2c/bibtex.test
+++ b/source/texk/web2c/bibtex.test
@@ -1,10 +1,11 @@
 #! /bin/sh -vx
-# $Id: bibtex.test 45809 2017-11-15 00:36:56Z karl $
+# $Id: bibtex.test 63238 2022-05-06 07:23:56Z takuji $
 # Copyright 2017 Karl Berry <tex-live@tug.org>
 # Copyright 2009 Peter Breitenlohner <tex-live@tug.org>
 # You may freely use, modify and/or distribute this file.
 
 test -d tests || mkdir -p tests
+rm -f tests/xexampl.*
 
 cp $srcdir/tests/exampl.aux tests/xexampl.aux
 
@@ -12,4 +13,5 @@ TEXMFCNF=$srcdir/../kpathsea \
   BSTINPUTS=$srcdir/tests \
   BIBINPUTS=$srcdir/tests \
   ./bibtex tests/xexampl || exit 1
+diff $srcdir/tests/exampl.bbl tests/xexampl.bbl || exit 2
 
diff --git a/source/texk/web2c/ctangleboot.cin b/source/texk/web2c/ctangleboot.cin
index 18ba68505ce5b47be6946e957b0cc211830594dd..9b2c18acc462f3f7f1e1061a4bfaf3b90eeb8f4c 100644
--- a/source/texk/web2c/ctangleboot.cin
+++ b/source/texk/web2c/ctangleboot.cin
@@ -28,7 +28,7 @@
 /*:4*/
 #line 67 "cwebdir/ctangle.w"
 
-#define banner "This is CTANGLE, Version 4.7" \
+#define banner "This is CTANGLE, Version 4.8" \
  \
 
 #define _(s) gettext(s)  \
diff --git a/source/texk/web2c/cwebdir/ChangeLog b/source/texk/web2c/cwebdir/ChangeLog
index a85748558a4cb1b30a35a6438c91e071e256a2ea..7aa44a8a43c18b6912b5983f0328a7a01f8f6f02 100644
--- a/source/texk/web2c/cwebdir/ChangeLog
+++ b/source/texk/web2c/cwebdir/ChangeLog
@@ -1,3 +1,131 @@
+2022-07-02  Andreas Scherer  <https://ascherer.github.io>
+
+	* cwebmac.tex,
+	* texinputs/pdfctwimac.tex: Fix \ifpdf logic.
+
+2022-06-20  Andreas Scherer  <https://ascherer.github.io>
+
+	* texinputs/dcwebmac.tex,
+	* texinputs/dcwebstrings.tex,
+	* texinputs/fcwebman.tex,
+	* texinputs/icwebmac.tex: Update translated macros.
+
+2022-06-19  Andreas Scherer  <https://ascherer.github.io>
+
+	* comm-w2c.ch,
+	* ctwill-mini.ch,
+	* cwebmac.tex,
+	* cwebman-w2c.ch,
+	* cwebman.tex,
+	* texinputs/dctproofmac.tex,
+	* texinputs/dctwimac.tex,
+	* texinputs/pdfctwimac.tex: Use 'iftex.sty' for TeX engine tests.
+
+2022-06-18  Andreas Scherer  <https://ascherer.github.io>
+
+	* cwebmac.tex: Fix \pdfURL for 'valid' output.
+
+2022-06-17  Andreas Scherer  <https://ascherer.github.io>
+
+	* Makefile: New target for CTAN packaging.
+	* cwebmac.tex: Fix two bugs from 2002 and 2000.
+
+2022-06-05  Andreas Scherer  <https://ascherer.github.io>
+
+	* Makefile,
+	* comm-mac.ch,
+	* comm-mini.ch,
+	* comm-ql.ch,
+	* comm-w2c.ch,
+	* comm-w2c.h,
+	* common.h,
+	* common.w,
+	* ctang-bs.ch,
+	* ctang-pc.ch,
+	* ctang-ql.ch,
+	* ctang-vms.ch,
+	* ctang-w2c.ch,
+	* ctang-w32.ch,
+	* ctangle.c,
+	* ctangle.w,
+	* ctwill-w2c.ch,
+	* cweav-bs.ch,
+	* cweav-pc.ch,
+	* cweav-ql.ch,
+	* cweav-vms.ch,
+	* cweav-w2c.ch,
+	* cweav-w32.ch,
+	* cweave.w,
+	* cwebmac.tex,
+	* cwebman-w2c.ch,
+	* cwebman.tex,
+	* po/cweb-tl.pot,
+	* po/cweb.pot,
+	* po/de/cweb-tl.po,
+	* po/de/cweb.po,
+	* po/it/cweb.po,
+	* prod-cweave.w,
+	* prod-twill.w,
+	* prod.w: CWEB 4.8 release.
+
+2022-06-05  Andreas Scherer  <https://ascherer.github.io>
+
+	* cweave.w,
+	* cwebman-w2c.ch,
+	* cwebman.tex: Update borderline cases.
+
+2022-06-04  Andreas Scherer  <https://ascherer.github.io>
+
+	* cwebman.tex: Update codes in Appendices.
+
+2022-05-31  Andreas Scherer  <https://ascherer.github.io>
+
+	* common.w: Amend reference to COMMON interface.
+
+2022-05-29  Andreas Scherer  <https://ascherer.github.io>
+
+	* cweave.w: DRY up section 120.
+
+2022-05-28  Andreas Scherer  <https://ascherer.github.io>
+
+	* ctwill-w2c.ch,
+	* cweav-w2c.ch,
+	* cweave.w,
+	* prod-cweave.w,
+	* prod-twill.w,
+	* prod.w: Improve productions 33, 118, and 153.
+
+2022-05-27  Andreas Scherer  <https://ascherer.github.io>
+
+	* ctwill-mini.ch,
+	* ctwill-w2c.ch,
+	* cweav-w2c.ch,
+	* cweave.w,
+	* prod-cweave.w,
+	* prod-twill.w,
+	* prod.w: Unambiguous prductions 83 and 121.
+
+2022-05-23  Andreas Scherer  <https://ascherer.github.io>
+
+	* Makefile,
+	* ctwill-w2c.ch,
+	* cweave.w,
+	* prod-cweave.w,
+	* prod.w: Fix production rule 117 similar to rule 35.
+
+2022-05-03  Andreas Scherer  <https://ascherer.github.io>
+
+	* cweave.w,
+	* prod-cweave.w,
+	* prod-twill.w,
+	* prod.w: Use non-breaking thin space for simple cast.
+
+2022-04-30  Andreas Scherer  <https://ascherer.github.io>
+
+	* ctwill-w2c.ch,
+	* cweav-w2c.ch,
+	* cweave.w: Fix a preprocessor issue (gb_io.w).
+
 2022-03-10  Andreas Scherer  <https://ascherer.github.io>
 
 	* Makefile,
diff --git a/source/texk/web2c/cwebdir/Makefile b/source/texk/web2c/cwebdir/Makefile
index d3bc422059b49856e740b1381317fb594bf06efc..08effe531d36a7bf8940bcc9f2746d686a49d556 100644
--- a/source/texk/web2c/cwebdir/Makefile
+++ b/source/texk/web2c/cwebdir/Makefile
@@ -1,6 +1,6 @@
 # This file is part of CWEB.
 # It is distributed WITHOUT ANY WARRANTY, express or implied.
-# Version 4.7 --- February 2022
+# Version 4.8 --- June 2022
 
 # Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
 
@@ -13,7 +13,7 @@
 # entire resulting derived work is given a different name and distributed
 # under the terms of a permission notice identical to this one.
 
-# 
+#
 # Read the README file, then edit this file to reflect local conditions
 #
 
@@ -138,7 +138,7 @@ common.o: common.c
 	$(CC) $(CFLAGS) -DCWEBINPUTS=\"$(CWEBINPUTS)\" -c common.c
 
 ctangle: ctangle.o common.o
-	$(CC) $(LINKFLAGS) -o ctangle ctangle.o common.o 
+	$(CC) $(LINKFLAGS) -o ctangle ctangle.o common.o
 
 ctangle.c: ctangle.w $(TCHANGES) common.h
 	$(CTANGLE) ctangle $(TCHANGES)
@@ -203,3 +203,8 @@ tarfile: $(ALL) examples
 
 tarball:
 	tar zcvhf /tmp/cweb.tgz $(ALL) examples
+
+ctan:
+	git archive -o ~/cweb-4.8.zip --prefix=cweb/ cweb-4.8
+	make PDF=pdf usermanual
+	cd ..; zip -r ~/cweb-4.8.zip cweb/cwebman.pdf
diff --git a/source/texk/web2c/cwebdir/comm-mac.ch b/source/texk/web2c/cwebdir/comm-mac.ch
index 6d795ff4e8e8abe0f94c13da2fc2d367d9011649..6bad610d1bd0320447c9ecf9ca5171070a4e2128 100644
--- a/source/texk/web2c/cwebdir/comm-mac.ch
+++ b/source/texk/web2c/cwebdir/comm-mac.ch
@@ -4,9 +4,9 @@ No changes to CTANGLE or CWEAVE are needed.
 (Contributed 13 Oct 2000 by AndPio@aol.com; slightly edited by Don Knuth)
 
 @x in limbo, change the title page document to specify Mac version
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
 @y
-  \centerline{(Version 4.7 for MacOS)}
+  \centerline{(Version 4.8 for MacOS)}
 @z
 
 @x section 23: Make input_ln accept \n, \r, \n\r, or \r\n as line endings
diff --git a/source/texk/web2c/cwebdir/comm-ql.ch b/source/texk/web2c/cwebdir/comm-ql.ch
index aa451c745568a47ce3d8667f7669c78783fc1076..52c4834bedf073801262ddceb65c34f755b59beb 100644
--- a/source/texk/web2c/cwebdir/comm-ql.ch
+++ b/source/texk/web2c/cwebdir/comm-ql.ch
@@ -10,24 +10,24 @@ ex <dev_>cc;'-v -h -c -=500000 -DCWEBINPUTS=flp2_ common_c'
 @x
 \def\v{\char'174} % vertical (|) in typewriter font
 
-\def\title{Common code for CTANGLE and CWEAVE (Version 4.7)}
+\def\title{Common code for CTANGLE and CWEAVE (Version 4.8)}
 \def\topofcontents{\null\vfill
   \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and
     {\ttitlefont CWEAVE}}
   \vskip 15pt
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
   \vfill}
 \def\botofcontents{\vfill
 \noindent
 @y
 \def\v{\char'174} % vertical (|) in typewriter font
 
-\def\title{Common code for CTANGLE and CWEAVE (QL Version 4.7)}
+\def\title{Common code for CTANGLE and CWEAVE (QL Version 4.8)}
 \def\topofcontents{\null\vfill
   \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and
     {\ttitlefont CWEAVE}}
   \vskip 15pt
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
   \vfill}
 \def\botofcontents{\vfill
 \noindent
diff --git a/source/texk/web2c/cwebdir/comm-w2c.ch b/source/texk/web2c/cwebdir/comm-w2c.ch
index fea407efbeb5b1d30d0b71f135075b61f1bab0e9..951d8063d693fa754bfee81b483afee2fd67ade1 100644
--- a/source/texk/web2c/cwebdir/comm-w2c.ch
+++ b/source/texk/web2c/cwebdir/comm-w2c.ch
@@ -17,16 +17,16 @@
 @q Please send comments, suggestions, etc. to tex-k@@tug.org.            @>
 
 @x
-\def\title{Common code for CTANGLE and CWEAVE (Version 4.7)}
+\def\title{Common code for CTANGLE and CWEAVE (Version 4.8)}
 @y
-\def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} \ifacro\sanitizecommand\Kpathsea{KPATHSEA}\fi
-\def\title{Common code for CTANGLE and CWEAVE (4.7 [\TeX~Live])}
+\def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} \ifpdf\sanitizecommand\Kpathsea{KPATHSEA}\fi
+\def\title{Common code for CTANGLE and CWEAVE (4.8 [\TeX~Live])}
 @z
 
 @x
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
 @y
-  \centerline{(Version 4.7 [\TeX~Live])}
+  \centerline{(Version 4.8 [\TeX~Live])}
 @z
 
 @x
diff --git a/source/texk/web2c/cwebdir/comm-w2c.h b/source/texk/web2c/cwebdir/comm-w2c.h
index e0c23709fcdefc0a9a9b30a6faea5e1fce799d7d..922e4aaee956dfbcf6d9d144333c1daa2bba81c9 100644
--- a/source/texk/web2c/cwebdir/comm-w2c.h
+++ b/source/texk/web2c/cwebdir/comm-w2c.h
@@ -2,7 +2,7 @@
 % This program by Silvio Levy and Donald E. Knuth
 % is based on a program by Knuth.
 % It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.7 --- February 2022 (works also with later versions)
+% Version 4.8 --- June 2022 (works also with later versions)
 
 % Copyright (C) 1987,1990,1993 Silvio Levy and Donald E. Knuth
 
diff --git a/source/texk/web2c/cwebdir/common.h b/source/texk/web2c/cwebdir/common.h
index 4d2caba05d7a6b1f57d43fcdd1dc818a9a5b4439..186e87fd63fc4920e3619aa6571fe9a7e0d19674 100644
--- a/source/texk/web2c/cwebdir/common.h
+++ b/source/texk/web2c/cwebdir/common.h
@@ -2,7 +2,7 @@
 % This program by Silvio Levy and Donald E. Knuth
 % is based on a program by Knuth.
 % It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.7 --- February 2022 (works also with later versions)
+% Version 4.8 --- June 2022 (works also with later versions)
 
 % Copyright (C) 1987,1990,1993 Silvio Levy and Donald E. Knuth
 
diff --git a/source/texk/web2c/cwebdir/common.w b/source/texk/web2c/cwebdir/common.w
index be110e5c334e5597ce935bd7958b09e75a146238..4ec17241c3b72a6ab2471d02b9935fc4c3bb0d44 100644
--- a/source/texk/web2c/cwebdir/common.w
+++ b/source/texk/web2c/cwebdir/common.w
@@ -2,7 +2,7 @@
 % This program by Silvio Levy and Donald E. Knuth
 % is based on a program by Knuth.
 % It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.7 --- February 2022
+% Version 4.8 --- June 2022
 
 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
 
@@ -22,12 +22,12 @@
 
 \def\v{\char'174} % vertical (|) in typewriter font
 
-\def\title{Common code for CTANGLE and CWEAVE (Version 4.7)}
+\def\title{Common code for CTANGLE and CWEAVE (Version 4.8)}
 \def\topofcontents{\null\vfill
   \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and
     {\ttitlefont CWEAVE}}
   \vskip 15pt
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
   \vfill}
 \def\botofcontents{\vfill
 \noindent
@@ -66,8 +66,8 @@ The file begins with a few basic definitions.
 @<Global variables@>@/
 @<Predeclaration of procedures@>
 
-@ The details will be filled in due course.  The interface of this module
-is included first.  It is also used by the main programs.
+@ The details will be filled in due course.  The interface |"common.h"| of this
+\.{COMMON} module is included first.  It is also used by the main programs.
 
 @i common.h
 
diff --git a/source/texk/web2c/cwebdir/ctang-bs.ch b/source/texk/web2c/cwebdir/ctang-bs.ch
index ecdb4be591ce4f6ad073045c18d398ac2f47361d..911d7d5dcf270f909c77b38f23621fd62487f4c2 100644
--- a/source/texk/web2c/cwebdir/ctang-bs.ch
+++ b/source/texk/web2c/cwebdir/ctang-bs.ch
@@ -17,12 +17,12 @@ by using "huge" pointers.
 The ``banner line'' defined here should be changed whenever \.{CTANGLE}
 is modified.
 
-@d banner "This is CTANGLE (Version 4.7)"
+@d banner "This is CTANGLE (Version 4.8)"
 @y
 The ``banner line'' defined here should be changed whenever \.{CTANGLE}
 is modified.
 
-@d banner "This is CTANGLE (Version 4.7pc/big)"
+@d banner "This is CTANGLE (Version 4.8pc/big)"
 @z
 
 
diff --git a/source/texk/web2c/cwebdir/ctang-pc.ch b/source/texk/web2c/cwebdir/ctang-pc.ch
index 459356a59eea01d10943a799b45c2a911a1844dd..00c193730705af929a1d0c97919af56f272b7097 100644
--- a/source/texk/web2c/cwebdir/ctang-pc.ch
+++ b/source/texk/web2c/cwebdir/ctang-pc.ch
@@ -9,9 +9,9 @@ that allows >64K arrays. If you need lots of bytes and toks, try the
 alternate change files with -bs suffix instead of -pc.
 
 @x section 1
-@d banner "This is CTANGLE (Version 4.7)"
+@d banner "This is CTANGLE (Version 4.8)"
 @y
-@d banner "This is CTANGLE (Version 4.7pc)"
+@d banner "This is CTANGLE (Version 4.8pc)"
 @z
 @x section 17
 @d max_bytes 100000 /* the number of bytes in identifiers,
diff --git a/source/texk/web2c/cwebdir/ctang-ql.ch b/source/texk/web2c/cwebdir/ctang-ql.ch
index 727b20ea9c87837ba4f2b6c524b4b016641050b9..809dff497db1ca0206d64efe1b5f22fff1915974 100644
--- a/source/texk/web2c/cwebdir/ctang-ql.ch
+++ b/source/texk/web2c/cwebdir/ctang-ql.ch
@@ -7,15 +7,15 @@ ex <dev_>cc;"-v -h -c -=500000 ctangle_c"
 
 
 @x
-\def\title{CTANGLE (Version 4.7)}
+\def\title{CTANGLE (Version 4.8)}
 @y
-\def\title{CTANGLE (QL Version 4.7)}
+\def\title{CTANGLE (QL Version 4.8)}
 @z
 
 @x section 1
-@d banner "This is CTANGLE (Version 4.7)"
+@d banner "This is CTANGLE (Version 4.8)"
 @y
-@d banner "This is CTANGLE (QL Version 4.7)"
+@d banner "This is CTANGLE (QL Version 4.8)"
 @z
 
 @x
diff --git a/source/texk/web2c/cwebdir/ctang-vms.ch b/source/texk/web2c/cwebdir/ctang-vms.ch
index e494b86f4fa0213b0d15d2759ad49dab8463f28d..a928d912cfdbcfe522a5eaebd939ce0fe31ebafd 100644
--- a/source/texk/web2c/cwebdir/ctang-vms.ch
+++ b/source/texk/web2c/cwebdir/ctang-vms.ch
@@ -10,9 +10,9 @@ created:
 (these changes not necessary for initial bootstrapping)
 
 @x section 1 (01-FEB-1992 ST)
-@d banner "This is CTANGLE (Version 4.7)"
+@d banner "This is CTANGLE (Version 4.8)"
 @y
-@d banner "This is CTANGLE (VAX/VMS Version 4.7)"
+@d banner "This is CTANGLE (VAX/VMS Version 4.8)"
 @z
 
 @x section 4 (01-FEB-1992 ST)
diff --git a/source/texk/web2c/cwebdir/ctang-w2c.ch b/source/texk/web2c/cwebdir/ctang-w2c.ch
index 6d74d1997a64bc68c6d43474a508ca7be266075f..9b115568f4f27cac4447ed05b30d59b9dc3d6695 100644
--- a/source/texk/web2c/cwebdir/ctang-w2c.ch
+++ b/source/texk/web2c/cwebdir/ctang-w2c.ch
@@ -17,15 +17,15 @@
 @q Please send comments, suggestions, etc. to tex-k@@tug.org.            @>
 
 @x
-\def\title{CTANGLE (Version 4.7)}
+\def\title{CTANGLE (Version 4.8)}
 @y
-\def\title{CTANGLE (Version 4.7 [\TeX~Live])}
+\def\title{CTANGLE (Version 4.8 [\TeX~Live])}
 @z
 
 @x
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
 @y
-  \centerline{(Version 4.7 [\TeX~Live])}
+  \centerline{(Version 4.8 [\TeX~Live])}
 @z
 
 @x
@@ -41,9 +41,9 @@
 @z
 
 @x
-@d banner "This is CTANGLE (Version 4.7)"
+@d banner "This is CTANGLE (Version 4.8)"
 @y
-@d banner "This is CTANGLE, Version 4.7"
+@d banner "This is CTANGLE, Version 4.8"
   /* will be extended by the \TeX~Live |versionstring| */
 @z
 
diff --git a/source/texk/web2c/cwebdir/ctang-w32.ch b/source/texk/web2c/cwebdir/ctang-w32.ch
index d54b84b9f9d7f51a706441f73883366a8bb885c6..ab76415410fcc4028601a61afe538929b35202e8 100644
--- a/source/texk/web2c/cwebdir/ctang-w32.ch
+++ b/source/texk/web2c/cwebdir/ctang-w32.ch
@@ -4,9 +4,9 @@ This is the change file for CWEB's CTANGLE under Win32
 Changes necessary for compiling with Borland C/C++
 
 @x section 1
-@d banner "This is CTANGLE (Version 4.7)"
+@d banner "This is CTANGLE (Version 4.8)"
 @y
-@d banner "This is CTANGLE (Version 4.7win32)"
+@d banner "This is CTANGLE (Version 4.8win32)"
 @z
 
 @x
diff --git a/source/texk/web2c/cwebdir/ctangle.c b/source/texk/web2c/cwebdir/ctangle.c
index bc00c0cf7f3187abf3992f70dbc3d68a01f65a7e..262469d6119230ccd7cc7c73dc7f9c3bac000e71 100644
--- a/source/texk/web2c/cwebdir/ctangle.c
+++ b/source/texk/web2c/cwebdir/ctangle.c
@@ -15,7 +15,7 @@
 /*:4*/
 #line 67 "ctangle.w"
 
-#define banner "This is CTANGLE (Version 4.7)" \
+#define banner "This is CTANGLE (Version 4.8)" \
 
 #define ctangle false
 #define cweave true \
diff --git a/source/texk/web2c/cwebdir/ctangle.w b/source/texk/web2c/cwebdir/ctangle.w
index 039f337865a746cb4ba97dc09d38f6c1e5f9b19b..0c372e59ed8cb7b20492eecd537f5426d766b9b2 100644
--- a/source/texk/web2c/cwebdir/ctangle.w
+++ b/source/texk/web2c/cwebdir/ctangle.w
@@ -2,7 +2,7 @@
 % This program by Silvio Levy and Donald E. Knuth
 % is based on a program by Knuth.
 % It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.7 --- February 2022
+% Version 4.8 --- June 2022
 
 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
 
@@ -27,11 +27,11 @@
 \mathchardef\RA="3221 % right arrow
 \mathchardef\BA="3224 % double arrow
 
-\def\title{CTANGLE (Version 4.7)}
+\def\title{CTANGLE (Version 4.8)}
 \def\topofcontents{\null\vfill
   \centerline{\titlefont The {\ttitlefont CTANGLE} processor}
   \vskip 15pt
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
   \vfill}
 \def\botofcontents{\vfill
 \noindent
@@ -61,7 +61,7 @@ Joachim Schrod, Lee Wittenberg, and others who have contributed improvements.
 The ``banner line'' defined here should be changed whenever \.{CTANGLE}
 is modified.
 
-@d banner "This is CTANGLE (Version 4.7)"
+@d banner "This is CTANGLE (Version 4.8)"
 
 @c
 @<Include files@>@/
diff --git a/source/texk/web2c/cwebdir/cweav-bs.ch b/source/texk/web2c/cwebdir/cweav-bs.ch
index c0fc3981ae5145409c4771bdc4ef747107c5072f..5a53a8ad0ae4a9e786d97c42347d8ab396c8b906 100644
--- a/source/texk/web2c/cwebdir/cweav-bs.ch
+++ b/source/texk/web2c/cwebdir/cweav-bs.ch
@@ -20,12 +20,12 @@ This file contributed by Barry Schwartz, trashman@crud.mn.org, 28 Jun 94.
 The ``banner line'' defined here should be changed whenever \.{CWEAVE}
 is modified.
 
-@d banner "This is CWEAVE (Version 4.7)"
+@d banner "This is CWEAVE (Version 4.8)"
 @y
 The ``banner line'' defined here should be changed whenever \.{CWEAVE}
 is modified.
 
-@d banner "This is CWEAVE (Version 4.7pc/big)"
+@d banner "This is CWEAVE (Version 4.8pc/big)"
 @z
 
 
diff --git a/source/texk/web2c/cwebdir/cweav-pc.ch b/source/texk/web2c/cwebdir/cweav-pc.ch
index 101b4d8a7ae2e48047107c95fd5eaa609006a0e6..eb2e3942659ec7e7e3b40bc35a6f57141e0c9aa5 100644
--- a/source/texk/web2c/cwebdir/cweav-pc.ch
+++ b/source/texk/web2c/cwebdir/cweav-pc.ch
@@ -11,9 +11,9 @@ that allows >64K arrays. (If you need lots more bytes, try the alternate
 change files that have -bs in their name instead of -pc.)
 
 @x section 1
-@d banner "This is CWEAVE (Version 4.7)"
+@d banner "This is CWEAVE (Version 4.8)"
 @y
-@d banner "This is CWEAVE (Version 4.7pc)"
+@d banner "This is CWEAVE (Version 4.8pc)"
 @z
 
 @x section 17
diff --git a/source/texk/web2c/cwebdir/cweav-ql.ch b/source/texk/web2c/cwebdir/cweav-ql.ch
index 041cba0a830e319ba8a4e886a2a28ac4d31b272c..94e1f54304f7c919bdcdcb516b925e7758d3fe0f 100644
--- a/source/texk/web2c/cwebdir/cweav-ql.ch
+++ b/source/texk/web2c/cwebdir/cweav-ql.ch
@@ -7,15 +7,15 @@ ex <dev_>cc;"-v -h -c =500000 cweave_c"
 
 
 @x
-\def\title{CWEAVE (Version 4.7)}
+\def\title{CWEAVE (Version 4.8)}
 @y
-\def\title{CWEAVE (QL Version 4.7)}
+\def\title{CWEAVE (QL Version 4.8)}
 @z
 
 @x section 1
-@d banner "This is CWEAVE (Version 4.7)"
+@d banner "This is CWEAVE (Version 4.8)"
 @y
-@d banner "This is CWEAVE (QL Version 4.7)"
+@d banner "This is CWEAVE (QL Version 4.8)"
 @z
 
 @x
diff --git a/source/texk/web2c/cwebdir/cweav-vms.ch b/source/texk/web2c/cwebdir/cweav-vms.ch
index 5078c16541d10f32eb5b25c2a622b13b50dc6db4..5debd4bd92e1fe7fabdd23840d6bd59ffc021034 100644
--- a/source/texk/web2c/cwebdir/cweav-vms.ch
+++ b/source/texk/web2c/cwebdir/cweav-vms.ch
@@ -13,9 +13,9 @@ modified:
 (also modified by Don Knuth to keep version numbers uptodate)
 
 @x section 1 (01-FEB-1992 ST)
-@d banner "This is CWEAVE (Version 4.7)"
+@d banner "This is CWEAVE (Version 4.8)"
 @y
-@d banner "This is CWEAVE (VAX/VMS Version 4.7)"
+@d banner "This is CWEAVE (VAX/VMS Version 4.8)"
 @z
 
 @x section 4 (01-FEB-1992 ST)
diff --git a/source/texk/web2c/cwebdir/cweav-w2c.ch b/source/texk/web2c/cwebdir/cweav-w2c.ch
index 2f776674788e21be967307ffedd7fa4b5b675bef..d7cb2065ec1bdd9f681699ad187a8be572b12940 100644
--- a/source/texk/web2c/cwebdir/cweav-w2c.ch
+++ b/source/texk/web2c/cwebdir/cweav-w2c.ch
@@ -17,15 +17,15 @@
 @q Please send comments, suggestions, etc. to tex-k@@tug.org.            @>
 
 @x
-\def\title{CWEAVE (Version 4.7)}
+\def\title{CWEAVE (Version 4.8)}
 @y
-\def\title{CWEAVE (Version 4.7 [\TeX~Live])}
+\def\title{CWEAVE (Version 4.8 [\TeX~Live])}
 @z
 
 @x
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
 @y
-  \centerline{(Version 4.7 [\TeX~Live])}
+  \centerline{(Version 4.8 [\TeX~Live])}
 @z
 
 @x
@@ -41,9 +41,9 @@
 @z
 
 @x
-@d banner "This is CWEAVE (Version 4.7)"
+@d banner "This is CWEAVE (Version 4.8)"
 @y
-@d banner "This is CWEAVE, Version 4.7"
+@d banner "This is CWEAVE, Version 4.8"
   /* will be extended by the \TeX~Live |versionstring| */
 @z
 
@@ -290,7 +290,7 @@ static void print_text(text_pointer p);
 
 @ @c
 #if 0
-@t\4\4@>static void
+static void
 print_text( /* prints a token list for debugging; not used in |main| */
 @z
 
@@ -309,13 +309,13 @@ print_text( /* prints a token list for debugging; not used in |main| */
 @x
 @<Cases for |exp|@>=
 if (cat1==lbrace || cat1==int_like || cat1==decl) {
-  make_underlined(pp); big_app1(pp); big_app(dindent);
+  make_underlined(pp); big_app(dindent); big_app1(pp);
   reduce(pp,1,fn_decl,0,1);
 }
 @y
 @<Cases for |exp|@>=
 if(cat1==lbrace || cat1==int_like || cat1==decl) {
-  make_underlined(pp); big_app1(pp); if (indent_param_decl) big_app(dindent);
+  make_underlined(pp); if (indent_param_decl) big_app(dindent); big_app1(pp);
   reduce(pp,1,fn_decl,0,1);
 }
 @z
@@ -323,7 +323,7 @@ if(cat1==lbrace || cat1==int_like || cat1==decl) {
 @x
 @ @<Cases for |decl_head|@>=
 if (cat1==comma) {
-  big_app2(pp); big_app(' '); reduce(pp,2,decl_head,-1,33);
+  big_app2(pp); app(opt); app('9'); reduce(pp,2,decl_head,-1,33);
 }
 else if (cat1==ubinop) {
   big_app1_insert(pp,'{'); big_app('}'); reduce(pp,2,decl_head,-1,34);
@@ -335,14 +335,14 @@ else if ((cat1==binop||cat1==colon) && cat2==exp && (cat3==comma ||
     cat3==semi || cat3==rpar))
   squash(pp,3,decl_head,-1,36);
 else if (cat1==cast) squash(pp,2,decl_head,-1,37);
-else if (cat1==lbrace || cat1==int_like || cat1==decl) {
+else if (cat1==int_like || cat1==lbrace || cat1==decl) {
   big_app(dindent); squash(pp,1,fn_decl,0,38);
 }
 else if (cat1==semi) squash(pp,2,decl,-1,39);
 @y
 @ @<Cases for |decl_head|@>=
 if (cat1==comma) {
-  big_app2(pp); big_app(' '); reduce(pp,2,decl_head,-1,33);
+  big_app2(pp); app(opt); app('9'); reduce(pp,2,decl_head,-1,33);
 }
 else if (cat1==ubinop) {
   big_app1_insert(pp,'{'); big_app('}');
@@ -355,7 +355,7 @@ else if ((cat1==binop||cat1==colon) && cat2==exp && (cat3==comma ||
     cat3==semi || cat3==rpar))
   squash(pp,3,decl_head,-1,36);
 else if (cat1==cast) squash(pp,2,decl_head,-1,37);
-else if (cat1==lbrace || cat1==int_like || cat1==decl) {
+else if (cat1==int_like || cat1==lbrace || cat1==decl) {
   if (indent_param_decl) big_app(dindent);
   squash(pp,1,fn_decl,0,38);
 }
@@ -412,10 +412,10 @@ else if (cat1==stmt) {
 @z
 
 @x
-  big_app1_insert(pp, (cat1==function || cat1==decl) ? big_force :
+  big_app1_insert(pp, (cat1==decl || cat1==function) ? big_force :
      force_lines ? force : break_space); reduce(pp,2,cat1,-1,76);
 @y
-  big_app1_insert(pp, (cat1==function || cat1==decl) ? @|
+  big_app1_insert(pp, (cat1==decl || cat1==function) ? @|
      ( order_decl_stmt ? big_force : force ) : @|
      ( force_lines ? force : break_space ) ); reduce(pp,2,cat1,-1,76);
 @z
diff --git a/source/texk/web2c/cwebdir/cweav-w32.ch b/source/texk/web2c/cwebdir/cweav-w32.ch
index 84e6a321a57ee43418f397b1577ba08a3e0b701f..f40d6e3195870508949b8f889d2918b543220350 100644
--- a/source/texk/web2c/cwebdir/cweav-w32.ch
+++ b/source/texk/web2c/cwebdir/cweav-w32.ch
@@ -2,9 +2,9 @@ This is the change file for CWEB's CWEAVE under Win32
 (Contributed by Fabrice Popineau, February 2002)
 
 @x section 1
-@d banner "This is CWEAVE (Version 4.7)"
+@d banner "This is CWEAVE (Version 4.8)"
 @y
-@d banner "This is CWEAVE (Version 4.7win32)"
+@d banner "This is CWEAVE (Version 4.8win32)"
 @z
 
 @x
diff --git a/source/texk/web2c/cwebdir/cweave.w b/source/texk/web2c/cwebdir/cweave.w
index f23baa3dabc6d66980a3f5bac401e219a35a08ca..7de3b4d1d9f02a58c88aa5721bef414395050f50 100644
--- a/source/texk/web2c/cwebdir/cweave.w
+++ b/source/texk/web2c/cwebdir/cweave.w
@@ -2,7 +2,7 @@
 % This program by Silvio Levy and Donald E. Knuth
 % is based on a program by Knuth.
 % It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.7 --- February 2022
+% Version 4.8 --- June 2022
 
 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
 
@@ -32,11 +32,11 @@
 \def\skipxTeX{\\{skip\_\TEX/}}
 \def\copyxTeX{\\{copy\_\TEX/}}
 
-\def\title{CWEAVE (Version 4.7)}
+\def\title{CWEAVE (Version 4.8)}
 \def\topofcontents{\null\vfill
   \centerline{\titlefont The {\ttitlefont CWEAVE} processor}
   \vskip 15pt
-  \centerline{(Version 4.7)}
+  \centerline{(Version 4.8)}
   \vfill}
 \def\botofcontents{\vfill
 \noindent
@@ -67,7 +67,7 @@ Crusius, and others who have contributed improvements.
 The ``banner line'' defined here should be changed whenever \.{CWEAVE}
 is modified.
 
-@d banner "This is CWEAVE (Version 4.7)"
+@d banner "This is CWEAVE (Version 4.8)"
 
 @c
 @<Include files@>@/
@@ -2317,10 +2317,14 @@ tokens, and intercalates a `\.{\$}' token if necessary.  When in
 doubt what to use, use |big_app|.
 
 @d app(a) *(tok_ptr++)=(token)(a)
+@#
 @d big_app2(a) big_app1(a);@+big_app1(a+1)
 @d big_app3(a) big_app2(a);@+big_app1(a+2)
 @d big_app4(a) big_app3(a);@+big_app1(a+3)
+@#
 @d big_app1_insert(p,c) big_app1(p);@+big_app(c);@+big_app1(p+1)
+@d big_app1_insert_str(p,s) big_app1(p);@+app_str(s);@+big_app1(p+1)
+@d big_app2_insert(p,c) big_app2(p);@+big_app(c);@+big_app2(p+2)
 
 @<Predecl...@>=
 static void app_str(const char *);@/
@@ -2356,6 +2360,11 @@ static int cur_mathness, init_mathness;
 understanding the format by comparing the code with the symbolic
 productions as they were listed earlier.
 
+@d begin_math if (cur_mathness==maybe_math) init_mathness=yes_math;
+              else if (cur_mathness==no_math) app_str("${}")
+@d end_math   if (cur_mathness==maybe_math) init_mathness=no_math;
+              else if (cur_mathness==yes_math) app_str("{}$")
+
 @c
 static void
 app_str(
@@ -2370,12 +2379,10 @@ token a)
 {
         if (a==' ' || (a>=big_cancel && a<=big_force) || a==dindent)
             /* non-math token */ {
-                if (cur_mathness==maybe_math) init_mathness=no_math;
-                else if (cur_mathness==yes_math) app_str("{}$");
+                end_math;
                 cur_mathness=no_math;
         } else {
-                if (cur_mathness==maybe_math) init_mathness=yes_math;
-                else if (cur_mathness==no_math) app_str("${}");
+                begin_math;
                 cur_mathness=yes_math;
         }
         app(a);
@@ -2387,13 +2394,11 @@ scrap_pointer a)
 {
   switch (a->mathness % 4) { /* left boundary */
   case (no_math):
-    if (cur_mathness==maybe_math) init_mathness=no_math;
-    else if (cur_mathness==yes_math) app_str("{}$");
+    end_math;
     cur_mathness=a->mathness / 4; /* right boundary */
     break;
   case (yes_math):
-    if (cur_mathness==maybe_math) init_mathness=yes_math;
-    else if (cur_mathness==no_math) app_str("${}");
+    begin_math;
     cur_mathness=a->mathness / 4; /* right boundary */
     break;
   case (maybe_math): /* no changes */ break;
@@ -2644,15 +2649,14 @@ to be performed.
 
 @<Cases for |exp|@>=
 if (cat1==lbrace || cat1==int_like || cat1==decl) {
-  make_underlined(pp); big_app1(pp); big_app(dindent);
+  make_underlined(pp); big_app(dindent); big_app1(pp);
   reduce(pp,1,fn_decl,0,1);
 }
 else if (cat1==unop) squash(pp,2,exp,-2,2);
 else if ((cat1==binop || cat1==ubinop) && cat2==exp)
         squash(pp,3,exp,-2,3);
 else if (cat1==comma && cat2==exp) {
-  big_app2(pp);
-  app(opt); app('9'); big_app1(pp+2); reduce(pp,3,exp,-2,4);
+  big_app2(pp); app(opt); app('9'); big_app1(pp+2); reduce(pp,3,exp,-2,4);
 }
 else if (cat1==lpar && cat2==rpar && cat3==colon) reduce(pp+3,0,base,0,5);
 else if (cat1==cast && cat2==colon) reduce(pp+2,0,base,0,5);
@@ -2676,7 +2680,7 @@ else if (cat1==colcol && cat2==int_like) squash(pp,3,int_like,-2,152);
 @ @<Cases for |lpar|@>=
 if ((cat1==exp||cat1==ubinop) && cat2==rpar) squash(pp,3,exp,-2,11);
 else if (cat1==rpar) {
-  big_app1(pp); app_str("\\,"); big_app1(pp+1);
+  big_app1_insert_str(pp,"\\,");
 @.\\,@>
   reduce(pp,2,exp,-2,12);
 }
@@ -2714,7 +2718,9 @@ if (cat1==binop) {
 @ @<Cases for |cast|@>=
 if (cat1==lpar) squash(pp,2,lpar,-1,21);
 else if (cat1==exp) {
-  big_app1_insert(pp,' '); reduce(pp,2,exp,-2,21);
+  big_app1_insert_str(pp,"\\,");
+@.\\,@>
+  reduce(pp,2,exp,-2,21);
 }
 else if (cat1==semi) reduce(pp,0,exp,-2,22);
 
@@ -2747,7 +2753,7 @@ else if (cat1==colcol) squash(pp,2,colcol,-1,32);
 
 @ @<Cases for |decl_head|@>=
 if (cat1==comma) {
-  big_app2(pp); big_app(' '); reduce(pp,2,decl_head,-1,33);
+  big_app2(pp); app(opt); app('9'); reduce(pp,2,decl_head,-1,33);
 }
 else if (cat1==ubinop) {
   big_app1_insert(pp,'{'); big_app('}'); reduce(pp,2,decl_head,-1,34);
@@ -2759,7 +2765,7 @@ else if ((cat1==binop||cat1==colon) && cat2==exp && (cat3==comma ||
     cat3==semi || cat3==rpar))
   squash(pp,3,decl_head,-1,36);
 else if (cat1==cast) squash(pp,2,decl_head,-1,37);
-else if (cat1==lbrace || cat1==int_like || cat1==decl) {
+else if (cat1==int_like || cat1==lbrace || cat1==decl) {
   big_app(dindent); squash(pp,1,fn_decl,0,38);
 }
 else if (cat1==semi) squash(pp,2,decl,-1,39);
@@ -2778,7 +2784,7 @@ else if (cat1==stmt || cat1==function) {
 @ @<Cases for |base|@>=
 if (cat1==int_like || cat1==exp) {
   if (cat2==comma) {
-    big_app1(pp); big_app(' '); big_app2(pp+1);
+    big_app1_insert(pp,' '); big_app1(pp+2);
     app(opt); app('9'); reduce(pp,3,base,0,42);
   }
   else if (cat2==lbrace) {
@@ -2797,7 +2803,7 @@ else if (cat1==exp||cat1==int_like) {
     big_app1_insert(pp,' ');
     if (cat2==semi) reduce(pp,2,decl_head,0,45);
     else {
-      big_app(' '); big_app1(pp+2);reduce(pp,3,struct_head,0,46);
+      big_app(' '); big_app1(pp+2); reduce(pp,3,struct_head,0,46);
     }
   }
   else if (cat2==colon) reduce(pp+2,0,base,2,47);
@@ -2819,7 +2825,7 @@ if ((cat1==decl || cat1==stmt || cat1==function) && cat2==rbrace) {
   reduce(pp,3,int_like,-2,49);
 }
 else if (cat1==rbrace) {
-  big_app1(pp); app_str("\\,"); big_app1(pp+1);
+  big_app1_insert_str(pp,"\\,");
 @.\\,@>
   reduce(pp,2,int_like,-2,50);
 }
@@ -2837,13 +2843,13 @@ else if (cat1==attr) {
 }
 
 @ @<Cases for |function|@>=
-if (cat1==function || cat1==decl || cat1==stmt) {
+if (cat1==stmt || cat1==decl || cat1==function) {
   big_app1_insert(pp,big_force); reduce(pp,2,cat1,-1,53);
 }
 
 @ @<Cases for |lbrace|@>=
 if (cat1==rbrace) {
-  big_app1(pp); app_str("\\,"); big_app1(pp+1);
+  big_app1_insert_str(pp,"\\,");
 @.\\,@>
   reduce(pp,2,stmt,-1,54);
 }
@@ -2946,8 +2952,8 @@ grouped together on the same line.
 force_lines=true;
 
 @ @<Cases for |stmt|@>=
-if (cat1==stmt||cat1==decl||cat1==function) {
-  big_app1_insert(pp, (cat1==function || cat1==decl) ? big_force :
+if (cat1==stmt || cat1==decl || cat1==function) {
+  big_app1_insert(pp, (cat1==decl || cat1==function) ? big_force :
      force_lines ? force : break_space); reduce(pp,2,cat1,-1,76);
 }
 
@@ -2956,13 +2962,13 @@ big_app(' '); squash(pp,1,stmt,-1,77);
 
 @ @<Cases for |lproc|@>=
 if (cat1==define_like) make_underlined(pp+2);
-if (cat1==else_like || cat1==if_like || cat1==define_like)
+if (cat1==if_like || cat1==else_like || cat1==define_like)
   squash(pp,2,lproc,0,78);
 else if (cat1==rproc) {
   app(inserted); squash(pp,2,insert,-1,79);
 } else if (cat1==exp || cat1==function) {
   if (cat2==rproc) {
-    app(inserted); big_app1(pp); big_app(' '); big_app2(pp+1);
+    app(inserted); big_app1_insert(pp,' '); big_app1(pp+2);
     reduce(pp,3,insert,-1,80);
   }
   else if (cat1==exp && cat2==exp && cat3==rproc) {
@@ -2979,8 +2985,8 @@ if (cat1==semi) {
 else reduce(pp,0,exp,-2,82);
 
 @ @<Cases for |insert|@>=
-if (cat1)
-  squash(pp,2,cat1,0,83);
+if (cat1==function)
+  squash(pp,2,function,0,83);
 
 @ @<Cases for |prelangle|@>=
 init_mathness=cur_mathness=yes_math;
@@ -2995,7 +3001,7 @@ app('>'); reduce(pp,1,binop,-2,85);
 
 @<Cases for |langle|@>=
 if (cat1==prerangle) {
-  big_app1(pp); app_str("\\,"); big_app1(pp+1);
+  big_app1_insert_str(pp,"\\,");
 @.\\,@>
   reduce(pp,2,cast,-1,86);
 }
@@ -3009,8 +3015,10 @@ else if ((cat1==struct_like) @|
   && (cat2==exp || cat2==int_like) @|
   && (cat3==comma || cat3==prerangle)) {
     make_underlined(pp+2); if (reserve_typenames) make_reserved(pp+2);
-    big_app2(pp); big_app(' '); big_app2(pp+2);
-    if (cat3==comma) reduce(pp,4,langle,0,153);
+    big_app2_insert(pp,' ');
+    if (cat3==comma) {
+      app(opt); app('9'); reduce(pp,4,langle,0,153);
+    }
     else reduce(pp,4,cast,-1,154);
   }
 
@@ -3093,21 +3101,19 @@ if ((cat1==int_like || cat1==cast) && (cat2==comma || cat2==semi))
 else if (cat1==int_like) {
   big_app1_insert(pp,' '); reduce(pp,2,typedef_like,0,116);
 }
-else if (cat1==exp && cat2!=lpar && cat2!=exp && cat2!=cast) {
+else if (cat1==exp && cat2!=lpar && cat2!=lbrack && cat2!=exp && cat2!=cast) {
   make_underlined(pp+1); make_reserved(pp+1);
   big_app1_insert(pp,' '); reduce(pp,2,typedef_like,0,117);
 }
-else if (cat1==comma) {
-  big_app2(pp); big_app(' '); reduce(pp,2,typedef_like,0,118);
-}
+else if (cat1==comma) squash(pp,2,typedef_like,0,118);
 else if (cat1==semi) squash(pp,2,decl,-1,119);
 else if (cat1==ubinop && (cat2==ubinop || cat2==cast)) {
   big_app('{'); big_app1_insert(pp+1,'}'); reduce(pp+1,2,cat2,0,120);
 }
 
 @ @<Cases for |delete_like|@>=
-if (cat1==lpar && cat2==rpar) {
-  big_app2(pp); app_str("\\,"); big_app1(pp+2);
+if (cat1==lbrack && cat2==rbrack) {
+  big_app1(pp); big_app1_insert_str(pp+1,"\\,");
 @.\\,@>
   reduce(pp,3,delete_like,0,121);
 }
@@ -3136,7 +3142,7 @@ else reduce(pp,0,lpar,-1,129);
 if (cat1==rbrack && cat2==rbrack) squash(pp,3,attr,-1,131);
 else if (cat1==exp) squash(pp,2,attr_head,0,132);
 else if (cat1==using_like && cat2==exp && cat3==colon) {
-  big_app2(pp); big_app(' '); big_app2(pp+2); big_app(' ');
+  big_app2_insert(pp,' '); big_app(' ');
   reduce(pp,4,attr_head,0,133);
 }
 else if (cat1==comma) squash(pp,2,attr_head,0,145);
@@ -3596,7 +3602,7 @@ section, it will be made into a scrap when |finish_C| is called.
 There's a known bug here, in cases where an adjacent scrap is
 |prelangle| or |prerangle|. Then the \TEX/ string can disappear
 when the \.{\\langle} or \.{\\rangle} becomes \.{<} or \.{>}.
-For example, if the user writes \.{\v x<@@ty@@>\v}, the \TEX/ string
+For example, if the user writes \.{\v x<@@ty@@><42\v}, the \TEX/ string
 \.{\\hbox\{y\}} eventually becomes part of an |insert| scrap, which is combined
 with a |prelangle| scrap and eventually lost. The best way to work around
 this bug is probably to enclose the \.{@@t...@@>} in \.{@@[...@@]} so that
@@ -3931,9 +3937,9 @@ make_output(void) /* outputs the equivalents of tokens */
           if ((a<indent && !(b==big_cancel&&a==' ')) @|
             || (a>big_force && a!=dindent)) break;
           switch (a) {
+          case dindent: c++; @=/* fall through */@>@;
           case indent: c++; break;
           case outdent: c--; break;
-          case dindent: c+=2; break;
           case opt: a=get_output();
           }
         }
diff --git a/source/texk/web2c/cwebdir/cwebmac.tex b/source/texk/web2c/cwebdir/cwebmac.tex
index 7b3163efd8515ca1fe343c0761d4269a1e80c3d5..85ac02587bb95ba1e986a1f64d83633397fae8cb 100644
--- a/source/texk/web2c/cwebdir/cwebmac.tex
+++ b/source/texk/web2c/cwebdir/cwebmac.tex
@@ -1,25 +1,19 @@
 % standard macros for CWEB listings (in addition to plain.tex)
-% Version 4.7--- January 2022
+% Version 4.9 --- June 2022
 \ifx\renewenvironment\undefined\else\endinput\fi % LaTeX will use other macros
-\xdef\fmtversion{\fmtversion+CWEB4.7}
-\chardef\cwebversion=4 \chardef\cwebrevision=7
-\newif\ifpdf
-\ifx\pdf+\pdftrue\fi
+\xdef\fmtversion{\fmtversion+CWEB4.9}
+\chardef\cwebversion=4 \chardef\cwebrevision=9
+\input iftex.sty % TeX engine tests
+\ifx\pdf+\pdftrue\fi % for plain TeX in combination with dvipdfm
 % Uncomment the following line if you want PDF goodies to be the default
 %\ifx\pdf-\else\pdftrue\fi
 \def\pdflinkcolor{0 0 1} % the RGB values for hyperlink color
-\ifx\undefined\XeTeXrevision\else\pdftrue\fi % XeTeX produces PDF output
-\newif\ifpdftex
-\ifx\pdfoutput\undefined \pdftexfalse \else\ifnum\pdfoutput=0 \pdftexfalse
-%\else \pdftextrue \pdfoutput=1 \input pdfcolor \let\setcolor\pdfsetcolor \fi\fi
-\else \pdftextrue \pdfoutput=1 % changed in 3.69
+\ifxetex\pdftrue\fi % XeTeX produces PDF output
+\ifpdftex % pdfTeX produces PDF output if \pdfoutput>0
   \def\Black{\pdfliteral{0 g 0 G}}  % use rgb colors for direct PDF output too
   \def\Blue{\pdfliteral{\pdflinkcolor\space rg \pdflinkcolor\space RG}}
-\fi\fi
-\newif\ifhint
-\ifx\HINTversion\undefined \hintfalse \else \hinttrue \fi
-\newif\ifacro \ifpdf\acrotrue\fi \ifpdftex\acrotrue\fi
-\newif\ifacrohint \ifacro\acrohinttrue\fi \ifhint\acrohinttrue\fi
+\fi
+\newif\ifacrohint \ifpdf\acrohinttrue\fi \ifhint\acrohinttrue\fi
 
 \let\:=\. % preserve a way to get the dot accent
  % (all other accents will still work as usual)
@@ -135,17 +129,17 @@
 \newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD
 \newtoks\toksE \newtoks\toksF \newtoks\usersanitizer
 \newcount\countA \countA=0 \newcount\countB \countB=0
-\newcount\countC \countC=0
+\newcount\countC \countC=0 \newcount\countD \countD=0
 \newif\iftokprocessed \newif\ifTnum \newif\ifinstr
 {\def\\{\global\let\spacechar= }\\ }
 
-\ifacro % The following are pdf macros
+\ifpdf % The following are pdf macros
 \def\thewidth{\the\wd0 \space}
 \def\theheight{\the\ht\strutbox\space}
 \def\thedepth{\the\dp\strutbox\space}
 \ifpdftex
   \ifx\pdfannotlink\undefined\let\pdfannotlink\pdfstartlink\fi% for pdfTeX 0.14
-  \def\pdflink#1#2{\hbox{\pdfannotlink height\ht\strutbox depth\dp\strutbox
+  \def\pdflink#1#2{\hbox{\pdfannotlink height \theheight depth \thedepth
     attr{/Border [0 0 0]} goto num #1 \Blue #1\Black\pdfendlink}} % changed 3.69
 \else\def\pdflink#1#2{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
     \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight
@@ -267,7 +261,7 @@
 \let\pdflink=\HINTlink
 \fi % End of HINT macros
 
-% Common macros for \ifacro and \ifhint
+% Common macros for \ifpdf and \ifhint
 \ifacrohint
 \def\pdfnote#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA}
 \def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}%
@@ -294,14 +288,14 @@
 \def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
 \fi % End of common macros
 
-\def\pdfURL#1#2{\ifpdftex\pdfannotlink height\ht\strutbox depth\dp\strutbox
-  attr {/Border [0 0 0]} user { /Type /Action /Subtype /Link /A
+\def\pdfURL#1#2{\ifpdf \ifpdftex\pdfannotlink height \theheight depth \thedepth
+    attr {/Border [0 0 0]} user { /Type /Annot /Subtype /Link /A
       << /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69
-  \else \ifpdf{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
-    \special{pdf: ec}}\special{pdf: ann width \thewidth\space height \theheight
-      \space depth \thedepth\space << /Border [0 0 0]
-      /Type /Action /Subtype /Link /A << /S /URI /URI (#2) >> >>}\box0\relax}%
-  \else #1 ({\tt#2})\fi\fi}
+  \else {\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
+    \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight
+      depth \thedepth << /Border [0 0 0] /Type /Annot /Subtype /Link /A
+      << /S /URI /URI (#2) >> >>}\box0\relax}\fi
+  \else #1 ({\tt#2})\fi}
 {\catcode`\~=12 \gdef\TILDE/{~}} % ~ in a URL
 {\catcode`\_=12 \gdef\UNDER/{_}} % _ in a URL
 
@@ -310,7 +304,7 @@
   \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50
   \ifhint\HINTlabel\fi% Start page before section
   \noindent{\let\*=\lapstar\bf\secstar.\quad}%
-  \ifacro \smash{\raise\baselineskip\hbox to0pt{\let\*=\empty
+  \ifpdf \smash{\raise\baselineskip\hbox to0pt{\let\*=\empty
     \ifpdftex \pdfdest num \secstar fith%
     \else \special{pdf: dest (\romannumeral\secstar)
       [ @thispage /FitH @ypos ]}\fi}}\fi}
@@ -343,7 +337,7 @@
 \outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section
   \vskip\intersecskip\startsection\ignorespaces}
 \outer\def\N#1#2#3.{% beginning of starred section
-  \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi
+  \ifpdf{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi
   \gdepth=#1\gtitle={#3}\MN{#2}%
   \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small
     \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi
@@ -352,11 +346,11 @@
   \edef\gtitletoks{\expandafter\stripprefix\meaning\gtitletoks}%
   \edef\next{\write\cont{\ZZ{\gtitletoks}{#1}{\secno}% write to contents file
    {\noexpand\the\pageno}{\the\toksE}}}\next % \ZZ{title}{depth}{sec}{page}{ss}
-  \ifpdftex\expandafter\xdef\csname curr#1\endcsname{\secno}
+  \ifpdf \ifpdftex\expandafter\xdef\csname curr#1\endcsname{\secno}
     \ifnum#1>0\countB=#1 \advance\countB by-1
-      \advancenumber{chunk\the\countB.\expnumber{curr\the\countB}}\fi\fi
-  \ifpdf\special{pdf: outline #1 << /Title (\the\toksE) /Dest
-    [ @thispage /FitH @ypos ] >>}\fi
+      \advancenumber{chunk\the\countB.\expnumber{curr\the\countB}}\fi
+  \else \special{pdf: outline #1 << /Title (\the\toksE) /Dest
+    [ @thispage /FitH @ypos ] >>}\fi \fi
   \ifon\startsection{\bf#3.\quad}\ignorespaces}
 \def\MN#1{\par % common code for \M, \N
   {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name
@@ -433,8 +427,8 @@
 \def\title{\expandafter\uppercase\expandafter{\jobname}}
 \def\topofcontents{\centerline{\titlefont\title}\vskip.7in
   \vfill} % this material will start the table of contents page
-\def\startpdf{\ifpdftex\pdfcatalog{/PageMode /UseOutlines}\else
-    \ifpdf{\special{pdf: docview << /PageMode /UseOutlines >>}}\fi\fi}
+\def\startpdf{\ifpdf \ifpdftex\pdfcatalog{/PageMode /UseOutlines}
+  \else {\special{pdf: docview << /PageMode /UseOutlines >>}}\fi\fi}
 \def\botofcontents{\vfill
   \centerline{\covernote}} % this material will end the table of contents page
 \def\covernote{}
@@ -495,7 +489,8 @@
 \def\fin{\par\vfill\eject % this is done when we are ending the index
   \ifpagesaved\null\vfill\eject\fi % output a null index column
   \if L\lr\else\null\vfill\eject\fi % finish the current page
-  \ifpdftex \makebookmarks \fi % added in Version 3.68
+  \ifpdf \ifpdftex \makebookmarks % added in Version 3.68
+    \countsections \fi\fi % and in Version 4.9
   \parfillskip 0pt plus 1fil
   \def\grouptitle{NAMES OF THE SECTIONS}
   \let\topsecno=\nullsec
@@ -508,10 +503,10 @@
   \def\U{\note{Used in section}} % crossref for use of a section
   \def\Us{\note{Used in sections}} % crossref for uses of a section
   \def\I{\par\hangindent 2em}\let\*=*
-  \ifacro \def\outsecname{Names of the sections} \let\Xpdf\X
+  \ifpdf \def\outsecname{Names of the sections} \let\Xpdf\X
 %  \ifpdftex \makebookmarks \pdfdest name {NOS} fitb % in versions < 3.68
   \ifpdftex \pdfdest name {NOS} fith % changed in version 3.69
-    \pdfoutline goto name {NOS} count -\secno {\outsecname}
+    \pdfoutline goto name {NOS} count -\the\countD {\outsecname}
     \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
       {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
       \pdfoutline goto num \the\toksA \expandafter{\the\toksE}}
@@ -524,6 +519,10 @@
   \fi\fi
   \readsections}
 \def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax}
+\def\countsections{\message{Number of named sections:}
+  {\def\I{\global\advance\countD by 1}\def\X##1\X{\relax}
+  \def\Q##1.{\relax}\def\Qs##1.{\relax}\def\U##1.{\relax}\def\Us##1.{\relax}
+  \readsections\relax}\message{\the\countD}}
 \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
   \else \csname#1\endcsname \fi} % Petr Olsak's macros from texinfo.tex
 \def\advancenumber#1{\countA=\expnumber{#1}\relax \advance\countA by1
@@ -551,7 +550,7 @@
       \ \ifhint
           \HINTlink{#3}{\romannumeral#3}% No page numbers in HINT
           \HINTcontents{#1}{#2}{#3}%
-        \else\ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi
+        \else\ifpdf\pdflink{#3}{\romannumeral#3}\else#3\fi
           \hbox to3em{\hss#4}\fi}}
 \def\consetup#1{\ifcase#1 \bf % depth -1 (@**)
   \or % depth 0 (@*)
diff --git a/source/texk/web2c/cwebdir/cwebman.tex b/source/texk/web2c/cwebdir/cwebman.tex
index a2df0fe44edce40645cc8e5564cf1dd5ede42a8b..218a85b589493e109b45810654884527f55f1041 100644
--- a/source/texk/web2c/cwebdir/cwebman.tex
+++ b/source/texk/web2c/cwebdir/cwebman.tex
@@ -2,7 +2,7 @@
 
 \def\tangref{3} % where the main explanation of CTANGLing is given
 \input cwebmac
-\acrofalse\pdffalse\pdftexfalse\hintfalse\acrohintfalse
+\pdffalse\acrohintfalse
 \def\page{\box255 } \normalbottom
 \parskip 0pt plus 1pt
 \def\RA{\char'31 } % right arrow
@@ -35,7 +35,7 @@
 
 \def\lheader{\mainfont\the\pageno\hfill\sc\runninghead\hfill}
 \def\rheader{\hfill\sc\runninghead\hfill\mainfont\the\pageno}
-\def\runninghead{{\tentt CWEB} USER MANUAL (VERSION 4.7)}
+\def\runninghead{{\tentt CWEB} USER MANUAL (VERSION 4.8)}
 
 % This verbatim mode assumes that ! marks are !! in the text being copied.
 \def\verbatim{\begingroup
@@ -49,7 +49,7 @@
 \null\vfill
 \centerline{\titlefont The {\ttitlefont CWEB} System of
     Structured Documentation}
-\vskip 18pt\centerline{(Version 4.7 --- February 2022)}
+\vskip 18pt\centerline{(Version 4.8 --- June 2022)}
 \vskip 24pt
 \centerline{\authorfont Donald E. Knuth and Silvio Levy}
 \vfill
@@ -1133,7 +1133,7 @@ are just \\{exp}s.  Thus it would tell \TEX/ to format
 `\X2:Argument declarations\X' on the same line as
 `$\\{main}(\\{argc},\39\\{argv}{}$)'.
 In this case you should help \.{CWEAVE} by putting `\.{@/}' after
-`\.{main(argc,argv)}'.
+`\.{main(argc,argv)}' (plus `\.{@t\\qquad@>}' for consistent indentation).
 
 \.{CWEAVE} automatically inserts a bit of extra space between declarations
 and the first apparent statement of a block. One way to defeat this
@@ -1337,6 +1337,7 @@ strncpy(change_buffer,buffer,(size_t)(limit-buffer+1));
 }
 
 /*:27*//*32:*/
+#line 296 "common.w"
 !endgroup
 \endgroup
 \vfill\eject
@@ -1344,7 +1345,7 @@ strncpy(change_buffer,buffer,(size_t)(limit-buffer+1));
 \def\runninghead{APPENDIX A --- TRANSLATION BY {\tentt CWEAVE}}
 
 Here is the corresponding excerpt from \.{common.tex}.
-(Code for section 31 is omitted for space reasons.)
+\ifhint\relax\else(Code for section 31 is omitted for space reasons.)\fi
 
 \vskip6pt
 \begingroup \def\tt{\eighttt} \baselineskip9pt
@@ -1390,7 +1391,7 @@ ${}\\{change\_line}\PP;{}$\6
 \&{if} ${}(\\{buffer}[\T{0}]\I\.{'@'}){}$\1\5
 \&{continue};\2\6
 \&{if} (\\{xisupper}(\\{buffer}[\T{1}]))\1\5
-${}\\{buffer}[\T{1}]\K\\{tolower}{}$((\&{int}) \\{buffer}[\T{1}]);\2\6
+${}\\{buffer}[\T{1}]\K\\{tolower}((\&{int})\,\\{buffer}[\T{1}]);{}$\2\6
 \&{if} ${}(\\{buffer}[\T{1}]\E\.{'x'}){}$\1\5
 \&{break};\2\6
 \&{if} ${}(\\{buffer}[\T{1}]\E\.{'y'}\V\\{buffer}[\T{1}]\E\.{'z'}\V\\{buffer}[%
@@ -1417,6 +1418,15 @@ ${}\{{}$\1\6
 \4${}\}{}$\2\5
 \&{while} ${}(\\{limit}\E\\{buffer}){}$;\par
 \U27.\fi
+!ifhint
+\M{31}\B\X31:Move \PB{\\{buffer}} and \PB{\\{limit}} to \PB{\\{change\_buffer}}
+and \PB{\\{change\_limit}}\X${}\E{}$\6
+$\\{change\_limit}\K\\{change\_buffer}+(\&{ptrdiff\_t})(\\{limit}-%
+\\{buffer});{}$\6
+${}\\{strncpy}(\\{change\_buffer},\39\\{buffer},\39(\&{size\_t})(\\{limit}-%
+\\{buffer}+\T{1})){}$;\par
+\Us27\ET32.\fi
+!fi
 !endgroup
 \endgroup
 \vfil\eject
@@ -1468,7 +1478,7 @@ ${}\\{change\_line}\PP;{}$\6
 \&{if} ${}(\\{buffer}[\T{0}]\I\.{'@'}){}$\1\5
 \&{continue};\2\6
 \&{if} (\\{xisupper}(\\{buffer}[\T{1}]))\1\5
-${}\\{buffer}[\T{1}]\K\\{tolower}{}$((\&{eight\_bits}) \\{buffer}[\T{1}]);\2\6
+${}\\{buffer}[\T{1}]\K\\{tolower}((\&{int})\,\\{buffer}[\T{1}]);{}$\2\6
 \&{if} ${}(\\{buffer}[\T{1}]\E\.{'x'}){}$\1\5
 \&{break};\2\6
 \&{if} ${}(\\{buffer}[\T{1}]\E\.{'y'}\V\\{buffer}[\T{1}]\E\.{'z'}\V\\{buffer}[%
@@ -1597,7 +1607,7 @@ $$\lpile{\.{\\def\\topofcontents\{\\null\\vfill}\cr
   \.{ { }\\titlefalse \% include headline on the contents page}\cr
   \.{ { }\\def\\rheader\{\\mainfont The \{\\tt CWEAVE\}{ }processor\\hfil\}}\cr
   \.{ { }\\centerline\{\\titlefont The \{\\ttitlefont CWEAVE\}{ }processor\}}\cr
-  \.{ { }\\vskip 15pt \\centerline\{(Version 4.7)\}{ }\\vfill\}}\cr}$$
+  \.{ { }\\vskip 15pt \\centerline\{(Version 4.8)\}{ }\\vfill\}}\cr}$$
 Redefining \.{\\rheader}, which is the headline for right-hand pages,
 suffices in this case to put the desired information at the top of the
 contents page.
diff --git a/source/texk/web2c/cwebdir/prod.w b/source/texk/web2c/cwebdir/prod.w
index ff33c57a46a3035a5b1b6a3922b6bf07fbbebf37..1e6cb6194ca8fa7e6f78b7303f4f8ec26f8af51c 100644
--- a/source/texk/web2c/cwebdir/prod.w
+++ b/source/texk/web2c/cwebdir/prod.w
@@ -2,7 +2,7 @@
 % This program by Silvio Levy and Donald E. Knuth
 % is based on a program by Knuth.
 % It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.7 --- February 2022
+% Version 4.8 --- June 2022
 %
 @ Here is a table of all the productions.  Each production that
 combines two or more consecutive scraps implicitly inserts a {\tt \$}
@@ -56,7 +56,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 |insert| & \altt\\{any} {\\{any} \\{any}} {\\{any} \\{any} \\{any}}
 & stmt; \4\4 \C{comment}\cr
 \+& |exp| \altt|lbrace| |int_like| |decl|
-    & |fn_decl| \altt|lbrace| |int_like| |decl| \hfill $F=E^*\,\\{din}$
+    & |fn_decl| \altt|lbrace| |int_like| |decl| \hfill $F=\\{din}\,E^*$
     & \malt {\\{main}(\,) $\{$}
             {$\\{main}(\\{ac},\\{av}){}$ \&{int} \\{ac};} \cr
 \+& |exp| |unop| & |exp| & $x\PP$ \cr
@@ -90,7 +90,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 \+& |binop| |binop| & |binop| \hfill
                         $|math_rel|\,\.\{B_1\.\}\.\{B_2\.\}\.\}$ & |>>=|\cr
 \+& |cast| \alt |lpar| |exp| & \alt |lpar| |exp| \hfill
-  \alt $CL$ $C\.\ E$ & \malt {$(\&{double})(x+2)$} {(\&{double}) $x$} \cr
+  \alt $CL$ $C\.{\\,}E$ & \malt {$(\&{double})(x+2)$} {(\&{double})\,$x$} \cr
 \+& |cast| |semi| & |exp| |semi| & |(int);|\cr
 \+& |sizeof_like| |cast| & |exp| & |sizeof (double)|\cr
 \+& |sizeof_like| |exp| & |exp| \hfill $S\.\ E$ & \&{sizeof} $x$\cr
@@ -109,7 +109,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 \+& |colcol| \alt|exp| |int_like| & \alt|exp| |int_like| \hfill
      |qualifier| $C$\alt$E$ $I$ & \&C\DC$x$\cr
 \+& |colcol| |colcol| & |colcol| & \&C\DC\&B\DC\cr
-\+& |decl_head| |comma| & |decl_head| \hfill $DC\.\ $ & \&{int} $x,{}$ \cr
+\+& |decl_head| |comma| & |decl_head| \hfill $DC$\,|opt|9 & \&{int} $x,{}$ \cr
 \+& |decl_head| |ubinop| & |decl_head| \hfill $D\.\{U\.\}$ & |int *|\cr
 \+\dagit& |decl_head| |exp| & |decl_head| \hfill $DE^*$ & \&{int} $x$ \cr
 \+& |decl_head| \alt|binop| |colon| |exp| \altt|comma| |semi| |rpar| &
@@ -118,7 +118,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
     & \malt {\&{int} $f(\&{int}\ x=2)$} {\&{int} $b$ : 1} \cr
 \+& |decl_head| |cast| & |decl_head| & \&{int} $f$(\&{int})\cr
 \+& |decl_head| \altt|int_like| |lbrace| |decl| & |fn_decl|
-                   \altt|int_like| |lbrace| |decl| \hfill $F=D\,\\{din}$
+                   \altt|int_like| |lbrace| |decl| \hfill $F=\\{din}\,D$
     & \&{long} \\{time}(\,) $\{$\cr
 \+& |decl_head| |semi| & |decl| & \&{int} $n$;\cr
 \+& |decl| |decl| & |decl| \hfill $D_1\,|force|\,D_2$
@@ -166,7 +166,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
        $|force|\,E\,\\{in}\,\\{bsp}\,S\,\\{out}\,|force|$
     & \&{else} $x=0;$\cr
 \+& |else_head| \alt|stmt| |exp| & |stmt| \hfill
-       $|force|\,E\,\\{bsp}\,|noop|\,|cancel|\,S\,\\{bsp}$
+       $|force|\,E\,\\{bsp}\,|noop|\,|cancel|\,S\,\\{force}$
     & $\!\!$ \&{else} $\{x=0;\}$\cr
 \+& |if_clause| |lbrace| & |if_head| |lbrace| & \&{if} ($x$) $\{$\cr
 \+& |if_clause| |stmt| |else_like| |if_like| & |if_like| \hfill
@@ -193,7 +193,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 \+& |case_like| |colon| & |tag| & |default:|\cr
 \+& |case_like| |exp| & |exp| \hfill $C\.\ E$ & |return 0|\cr
 \+& |catch_like| \alt|cast| |exp| & |fn_decl| \hfill
-    $C$\alt $C$ $E$ \unskip \\{din} & |catch (...)|\cr
+    $C$\,\\{din}\,\alt $C$ $E$ & |catch (...)|\cr
 \+& |tag| |tag| & |tag| \hfill $T_1\,\\{bsp}\,T_2$ & |case 0: case 1:|\cr
 \+& |tag| \altt|stmt| |decl| |function| & \altt|stmt| |decl| |function|
        \hfill $|force|\,\\{back}\,T\,\\{bsp}\,S$
@@ -212,7 +212,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 \+& |section_scrap| |semi| & |stmt|\hfill $MS$ |force|
    &$\langle\,$section name$\,\rangle$;\cr
 \+& |section_scrap| & |exp| &$\langle\,$section name$\,\rangle$\cr
-\+& |insert| \\{any} & \\{any} & \.{\v\#include\v}\cr
+\+& |insert| |function| & |function| & \#\&{include} before \\{main}\cr
 \+& |prelangle| & |binop| \hfill \.< & $<$ not in template\cr
 \+& |prerangle| & |binop| \hfill \.> & $>$ not in template\cr
 \+& |langle| |prerangle| & |cast| \hfill $L\.{\\,}P$ & $\langle\,\rangle$\cr
@@ -262,14 +262,14 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
     \&{typedef} \&{char}\cr
 \+\dagit& |typedef_like| |exp| & |typedef_like| \hfill $T\.\ E^{**}$ &
     \&{typedef} \&I \.{@@[@@]} (|*|\&P)\cr
-\+& |typedef_like| |comma| & |typedef_like| \hfill $TC\.\ $ &
+\+& |typedef_like| |comma| & |typedef_like| \hfill $TC$ &
     \&{typedef} \&{int} \&x,\cr
-\+& |typedef_like| |semi| & |decl| & \&{typedef} \&{int} $\&x,\&y$;\cr
-\+& |typedef_like| |ubinop| \alt |cast| |ubinop| & 
+\+& |typedef_like| |semi| & |decl| & \&{typedef} \&{int} $\&x,{}$ $\&y$;\cr
+\+& |typedef_like| |ubinop| \alt |cast| |ubinop| &
     |typedef_like| \alt |cast| |ubinop| \hfill
     \alt $C=\.\{U\.\}C$ $U_2=\.\{U_1\.\}U_2$ \unskip &
     \&{typedef} |*|{}|*|(\&{CPtr})\cr
-\+& |delete_like| |lpar| |rpar| & |delete_like|\hfill $DL\.{\\,}R$
+\+& |delete_like| |lbrack| |rbrack| & |delete_like|\hfill $DL\.{\\,}R$
     & \&{delete}[\,] \cr
 \+& |delete_like| |exp| & |exp| \hfill $D\.\ E$ & \&{delete} $p$ \cr
 \+\dagit& |question| |exp| \alt |colon| |base| & |binop|
@@ -296,7 +296,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 \+& |using_like| & |int_like| & \&{using} not in attributes \cr
 \+& |struct_like| |attr| & |struct_like| \hfill $S\.\ A$
     & \&{struct} [[\\{deprecated}]]\cr
-\+& |exp| |attr| & |attr| \hfill $E\.\ A$ & \&{enum} $\{x\ [[\ldots]]\}$ \cr
+\+& |exp| |attr| & |exp| \hfill $E\.\ A$ & \&{enum} $\{x\ [[\ldots]]\}$ \cr
 \+& |attr| |typedef_like| & |typedef_like| \hfill $A\.\ T$
     & |[[deprecated]] typedef| \cr
 \+& |raw_int| |lbrack| & |exp| & |int[3]| \cr
@@ -313,10 +313,10 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 \+& |exp| |colcol| |int_like| & |int_like| & $\\{std}\DC\&{atomic}$ \cr
 \advance\midcol-30pt
 \+\dagit& |langle| |struct_like| \alt |exp| |int_like| |comma| &
-  |langle| \hfill $LS$\alt $E^{**}$ $I^{**}$ \unskip $C$
+  |langle| \hfill $LS$\.\ \alt $E^{**}$ $I^{**}$ \unskip $C$\,\\{opt}9
    & $\langle$\&{typename} $t,$\cr
 \+\dagit& |langle| |struct_like| \alt |exp| |int_like| |prerangle| &
-  |cast| \hfill $LS$\alt $E^{**}$ $I^{**}$ \unskip $P$
+  |cast| \hfill $LS$\.\ \alt $E^{**}$ $I^{**}$ \unskip $P$
     & $\langle$\&{typename} $t\rangle$ \cr
 \advance\midcol30pt
 \+& |template_like| |cast| |struct_like| & |struct_like| \hfill $T\.\ CS$ &
@@ -329,7 +329,7 @@ We use \\{in}, \\{out}, \\{back}, \\{bsp}, and \\{din} as shorthands for
 \parindent=0pt
 \dag{\bf Notes}
 \yskip
-Rule 35: The |exp| must not be immediately followed by |lpar|, |lbrack|,
+Rules 35, 117: The |exp| must not be immediately followed by |lpar|, |lbrack|,
 |exp|, or~|cast|.
 
 Rule 48: The |exp| or |int_like| must not be immediately followed by |base|.
@@ -352,9 +352,6 @@ must not be immediately followed by a |binop|.
 Rule 114: The |operator_like| must not be immediately followed by
 |raw_ubin|.
 
-Rule 117: The |exp| must not be immediately followed by |lpar|, |exp|,
-or |cast|.
-
 Rule 123: The mathness of the |colon| or |base| changes to `yes'.
 
 Rules 153, 154: |make_reserved| is called only if \.{CWEAVE} has been invoked
diff --git a/source/texk/web2c/help.h b/source/texk/web2c/help.h
index ce384d7793943bd81e97d28aa0bd872e8de737db..668d3f9f98c293471d70f8729c00a6ef2e75570d 100644
--- a/source/texk/web2c/help.h
+++ b/source/texk/web2c/help.h
@@ -373,6 +373,7 @@ const_string PBIBTEXHELP[] = {
     "  Write bibliography for entries in AUXFILE to AUXFILE.bbl,",
     "  along with a log file AUXFILE.blg."
     "",
+    "[-no]-guess-input-enc  disable/enable to guess input file encoding",
     "-kanji=STRING          set Japanese encoding (STRING=euc|jis|sjis|utf8)",
     "-min-crossrefs=NUMBER  include item after NUMBER cross-refs; default 2",
     "-terse                 do not print progress reports",
@@ -533,6 +534,7 @@ const_string UPBIBTEXHELP[] = {
     "  Write bibliography for entries in AUXFILE to AUXFILE.bbl,",
     "  along with a log file AUXFILE.blg."
     "",
+    "[-no]-guess-input-enc  disable/enable to guess input file encoding",
     "-kanji=STRING          set Japanese encoding (STRING=euc|jis|sjis|utf8|uptex)",
     "-kanji-internal=STRING set Japanese internal encoding (STRING=euc|uptex)",
     "-min-crossrefs=NUMBER  include item after NUMBER cross-refs; default 2",
diff --git a/source/texk/web2c/lib/ChangeLog b/source/texk/web2c/lib/ChangeLog
index 7516c488f7057afe2c2b74ed22c1c9756122bed1..127c93436da81e72bba410bfaef8bd4111d5b8ed 100644
--- a/source/texk/web2c/lib/ChangeLog
+++ b/source/texk/web2c/lib/ChangeLog
@@ -1,3 +1,13 @@
+2022-06-12  TANAKA Takuji  <ttk@t-lab.opal.ne.jp>
+
+	* texmfmp.c:
+	Support guessing input file encodings for pTeX family.
+	https://github.com/texjporg/tex-jp-build/issues/142
+
+2022-04-25  Karl Berry  <karl@freefriends.org>
+
+	* texmfmp.c (find_input_file): doc fix.
+
 2022-03-21  Karl Berry  <karl@tug.org>
 
 	* TL'22 release.
diff --git a/source/texk/web2c/lib/texmfmp.c b/source/texk/web2c/lib/texmfmp.c
index 6dc2b22119a07cfe1cb4cdea8b9b649f71ee843c..d75ebcd7e15fab3b482e6b15b8f8f3add59d5ce0 100644
--- a/source/texk/web2c/lib/texmfmp.c
+++ b/source/texk/web2c/lib/texmfmp.c
@@ -1836,10 +1836,8 @@ static struct option long_options[]
       { "no-mktex",                  1, 0, 0 },
 #endif /* TeX or MF */
 #if IS_pTeX
-#ifdef WIN32
       { "guess-input-enc",           0, &infile_enc_auto, 1 },
       { "no-guess-input-enc",        0, &infile_enc_auto, 0 },
-#endif
       { "kanji",                     1, 0, 0 },
       { "kanji-internal",            1, 0, 0 },
 #endif /* IS_pTeX */
@@ -3414,12 +3412,11 @@ find_input_file(integer s)
 #endif
     /* Look in -output-directory first, if the filename is not
        absolute.  This is because we want the pdf* functions to
-       be able to find the same files as \openin */
+       be able to find the same files as \openin.  */
     if (output_directory && !kpse_absolute_p (filename, false)) {
-        string pathname;
-
-        pathname = concat3(output_directory, DIR_SEP_STRING, filename);
-        if (!access(pathname, R_OK) && !dir_p (pathname)) {
+        string pathname = concat3(output_directory, DIR_SEP_STRING, filename);
+        /* If there happens to be a directory by that name, never mind.  */
+        if (access(pathname, R_OK) == 0 && !dir_p (pathname)) {
 #if IS_pTeX && !defined(WIN32)
             if (fname1) free(filename);
 #endif
diff --git a/source/texk/web2c/luatexdir/luatex_svnversion.h b/source/texk/web2c/luatexdir/luatex_svnversion.h
index c143acff89d1f24ed8fd2d6b387b77faf2f7bf5d..a37a3c3f428c4001443a1174a2d1078721bb8800 100644
--- a/source/texk/web2c/luatexdir/luatex_svnversion.h
+++ b/source/texk/web2c/luatexdir/luatex_svnversion.h
@@ -1,4 +1,4 @@
 #ifndef luatex_svn_revision_h
 #define luatex_svn_revision_h
-#define luatex_svn_revision 7528
+#define luatex_svn_revision 7529
 #endif
diff --git a/source/texk/web2c/man/ChangeLog b/source/texk/web2c/man/ChangeLog
index 9453f6cca144d0028af828e78c8e0cb5bbd66ef5..95a11e7d444964d5d40ff967c5fc5a153d6486f6 100644
--- a/source/texk/web2c/man/ChangeLog
+++ b/source/texk/web2c/man/ChangeLog
@@ -1,3 +1,7 @@
+2022-06-05  Andreas Scherer  <https://ascherer.github.io>
+
+	* ctwill.man: CWEB 4.8 release.
+
 2022-03-27  Andreas Scherer  <https://ascherer.github.io>
 
 	* tangle.man,
diff --git a/source/texk/web2c/man/ctwill.man b/source/texk/web2c/man/ctwill.man
index 0965faa9d5cc5469b61b2231af3853afda80937d..ed7a3757cadb4e50e5df051b3531d09f506dd314 100644
--- a/source/texk/web2c/man/ctwill.man
+++ b/source/texk/web2c/man/ctwill.man
@@ -14,7 +14,7 @@
 . ftr VB CB
 . ftr VBI CBI
 .\}
-.TH "CTWILL" "1" "February 5, 2022" "Web2c @VERSION@" "General Commands Manual"
+.TH "CTWILL" "1" "June 5, 2022" "Web2c @VERSION@" "General Commands Manual"
 .hy
 .SH NAME
 .PP
@@ -125,7 +125,7 @@ The present incarnation of \f[B]ctwill\f[R] and its utilities tries hard
 to be a drop-in replacement for the original package.
 There are, however, a few differences worth noting:
 .IP \[bu] 2
-This version is based on the most recent version of CWEB (4.7).
+This version is based on the most recent version of CWEB (4.8).
 .IP \[bu] 2
 In TeX\ Live the utility programs are prefixed with \f[B]ctwill-\f[R]
 and the macro files with \f[B]ct\f[R] for technical reasons.
diff --git a/source/texk/web2c/ptexdir/am/ptex.am b/source/texk/web2c/ptexdir/am/ptex.am
index d6ff7a3eaa171acdc62e8fb15b4302c1d880ea39..7f8139a140f326d4b520966f73bb02b0ff5bac9f 100644
--- a/source/texk/web2c/ptexdir/am/ptex.am
+++ b/source/texk/web2c/ptexdir/am/ptex.am
@@ -1,4 +1,4 @@
-## $Id: ptex.am 61575 2022-01-11 22:47:10Z karl $
+## $Id: ptex.am 63557 2022-06-12 08:18:23Z takuji $
 ## texk/web2c/ptexdir/am/ptex.am: Makefile fragment for pTeX.
 ##
 ## Copyright 2015-2022 Karl Berry <tex-live@tug.org>
@@ -211,7 +211,13 @@ endif PWEB
 EXTRA_DIST += ptexdir/tests/nissya_bib.aux ptexdir/tests/nissya.bst ptexdir/tests/sample.bib
 DISTCLEANFILES += ptests/nissya_bib.*
 ## ptexdir/pbibtex.test
-DISTCLEANFILES += ptests/xexampl.aux ptests/xexampl.bbl ptests/xexampl.blg
+EXTRA_DIST += tests/testfield.bst tests/enc-asc.bib tests/enc-jis.bib tests/enc-sjis.bib \
+	tests/enc-euc.bib tests/enc-utf8.bib tests/enc-utf8a.bib tests/enc-utf8b.bib \
+	tests/enc-amb0.bib tests/enc-amb1.bib tests/enc-amb2.bib \
+	tests/enc.aux tests/enc-e.aux tests/enc-s.aux \
+	tests/enc-p.bbl tests/enc-ep.bbl tests/enc-sp.bbl
+DISTCLEANFILES += ptests/xexampl.aux ptests/xexampl.bbl ptests/xexampl.blg \
+	ptests/xenc*.*
 ## ptexdir/pbibtex-mem.test
 EXTRA_DIST += tests/memdata1.bst tests/memdata2.bst tests/memdata3.bst
 DISTCLEANFILES += ptests/memtest.bib ptests/memtest?.*
diff --git a/source/texk/web2c/synctexdir/ChangeLog b/source/texk/web2c/synctexdir/ChangeLog
index da03ff9b67249979331a57f74a7f1fdb840f52d0..4af445b8614b9b790dde473db37f21016a359c70 100644
--- a/source/texk/web2c/synctexdir/ChangeLog
+++ b/source/texk/web2c/synctexdir/ChangeLog
@@ -1,3 +1,9 @@
+2022-05-31  Karl Berry  <karl@freefriends.org>
+
+	* synctex.c (synctexterminate): insert missing \ne
+	before SyncTeX written on "..." (quoted case).
+	Report from jerome.lelong, tex-k 31 May 2022 16:34:44.
+
 2022-03-21  Karl Berry  <karl@tug.org>
 
 	* TL'22 release.
diff --git a/source/texk/web2c/synctexdir/synctex.c b/source/texk/web2c/synctexdir/synctex.c
index 4ec54a4494e1a7619143a7c0123e9c4efe3367f4..b9b5d8a24f7d4c1635bf3f8ce130ec800d49e126 100644
--- a/source/texk/web2c/synctexdir/synctex.c
+++ b/source/texk/web2c/synctexdir/synctex.c
@@ -969,7 +969,7 @@ void synctexterminate(boolean log_opened)
 #ifdef W32UPTEXSYNCTEX
                         {
                         char *stmp = chgto_oem(tmp);
-                        printf((synctex_ctxt.flags.quoted ? "SyncTeX written on \"%s\"\n" : "\nSyncTeX written on %s.\n"),
+                        printf((synctex_ctxt.flags.quoted ? "\nSyncTeX written on \"%s\"\n" : "\nSyncTeX written on %s.\n"),
                                stmp);
                         free(stmp);
                         }
diff --git a/source/texk/web2c/texmfmp-help.h b/source/texk/web2c/texmfmp-help.h
index 1e720340c42f99add296d0f2978a5320a52ff80a..068cbe2da6b005d4c68e8d8d8591832439c2ecb8 100644
--- a/source/texk/web2c/texmfmp-help.h
+++ b/source/texk/web2c/texmfmp-help.h
@@ -99,9 +99,7 @@ const_string EPTEXHELP[] = {
     "-cnf-line=STRING        parse STRING as a configuration file line",
     "-etex                   enable e-TeX extensions",
     "-fmt=NAME               use NAME instead of program name or %&format.",
-#if defined(WIN32)
     "[-no]-guess-input-enc   disable/enable to guess input file encoding",
-#endif
     "-halt-on-error          stop processing at the first error",
     "[-no]-file-line-error   disable/enable file:line:error style messages",
     "-ini                    be iniptex.",
@@ -228,9 +226,7 @@ const_string EUPTEXHELP[] = {
     "-cnf-line=STRING        parse STRING as a configuration file line",
     "-etex                   enable e-TeX extensions",
     "-fmt=NAME               use NAME instead of program name or %&format.",
-#if defined(WIN32)
     "[-no]-guess-input-enc   disable/enable to guess input file encoding",
-#endif
     "-halt-on-error          stop processing at the first error",
     "[-no]-file-line-error   disable/enable file:line:error style messages",
     "-ini                    be iniptex.",
@@ -497,9 +493,7 @@ const_string PTEXHELP[] = {
     "",
     "-cnf-line=STRING        parse STRING as a configuration file line",
     "-fmt=NAME               use NAME instead of program name or %&format.",
-#if defined(WIN32)
     "[-no]-guess-input-enc   disable/enable to guess input file encoding",
-#endif
     "-halt-on-error          stop processing at the first error",
     "[-no]-file-line-error   disable/enable file:line:error style messages",
     "-ini                    be iniptex.",
@@ -624,9 +618,7 @@ const_string UPTEXHELP[] = {
     "",
     "-cnf-line=STRING        parse STRING as a configuration file line",
     "-fmt=NAME               use NAME instead of program name or %&format.",
-#if defined(WIN32)
     "[-no]-guess-input-enc   disable/enable to guess input file encoding",
-#endif
     "-halt-on-error          stop processing at the first error",
     "[-no]-file-line-error   disable/enable file:line:error style messages",
     "-ini                    be iniptex.",
diff --git a/source/texk/web2c/uptexdir/am/uptex.am b/source/texk/web2c/uptexdir/am/uptex.am
index 86f2e82ce9e11d5845856e19cc74d480092fe65d..5204e2623f51bd5ff30d08c1b693ca662b471290 100644
--- a/source/texk/web2c/uptexdir/am/uptex.am
+++ b/source/texk/web2c/uptexdir/am/uptex.am
@@ -1,4 +1,4 @@
-## $Id: uptex.am 61784 2022-01-29 10:40:22Z hironobu $
+## $Id: uptex.am 63557 2022-06-12 08:18:23Z takuji $
 ## texk/web2c/uptexdir/am/uptex.am: Makefile fragment for upTeX.
 ##
 ## Copyright 2016-2022 Karl Berry <tex-live@tug.org>
@@ -199,7 +199,9 @@ endif UPWEB
 ## Test data and results
 ##
 ## uptexdir/upbibtex.test
-DISTCLEANFILES += uptests/xexampl.aux uptests/xexampl.bbl uptests/xexampl.blg
+EXTRA_DIST += tests/enc-u.bbl tests/enc-eu.bbl tests/enc-su.bbl
+DISTCLEANFILES += uptests/xexampl.aux uptests/xexampl.bbl uptests/xexampl.blg \
+	uptests/xenc*.*
 ## uptexdir/updvitype.test
 DISTCLEANFILES += uptests/xstory.dvityp uptests/xpagenum.typ
 ## uptexdir/uppltotf.test