From a962a7d36195d88f6e41bda96fdce65a4d306058 Mon Sep 17 00:00:00 2001
From: Luigi Scarso <luigi.scarso@gmail.com>
Date: Wed, 12 Mar 2025 00:34:53 +0100
Subject: [PATCH] Sync with TeXLive rev. 74578.

---
 source/ChangeLog                              |    8 +
 source/README                                 |   31 +-
 source/configure                              | 5439 +++++------------
 source/libs/README                            |    6 +-
 source/libs/configure                         |   20 +-
 source/libs/harfbuzz/ChangeLog                |    5 +
 source/libs/harfbuzz/TLpatches/ChangeLog      |    5 +
 source/libs/harfbuzz/TLpatches/TL-Changes     |    4 +-
 source/libs/harfbuzz/configure                |   24 +-
 .../libs/harfbuzz/harfbuzz-src/CMakeLists.txt |    8 +-
 source/libs/harfbuzz/harfbuzz-src/NEWS        |   66 +-
 source/libs/harfbuzz/harfbuzz-src/README.md   |    6 +-
 source/libs/harfbuzz/harfbuzz-src/meson.build |   11 +-
 .../harfbuzz-src/src/OT/Color/CBDT/CBDT.hh    |    4 +-
 .../harfbuzz-src/src/OT/Color/COLR/COLR.hh    |   67 +-
 .../harfbuzz-src/src/OT/Color/CPAL/CPAL.hh    |    8 +
 .../src/OT/Layout/Common/Coverage.hh          |   22 +
 .../src/OT/Layout/Common/CoverageFormat1.hh   |    2 +
 .../src/OT/Layout/Common/CoverageFormat2.hh   |    2 +
 .../src/OT/Layout/GPOS/PairPosFormat1.hh      |   44 +-
 .../src/OT/Layout/GPOS/PairPosFormat2.hh      |   58 +-
 .../src/OT/Layout/GPOS/SinglePosFormat1.hh    |    2 +-
 .../src/OT/Layout/GPOS/SinglePosFormat2.hh    |    2 +-
 .../OT/Layout/GSUB/AlternateSubstFormat1.hh   |    2 +-
 .../OT/Layout/GSUB/LigatureSubstFormat1.hh    |   45 +-
 .../OT/Layout/GSUB/MultipleSubstFormat1.hh    |    2 +-
 .../GSUB/ReverseChainSingleSubstFormat1.hh    |    2 +-
 .../src/OT/Layout/GSUB/SingleSubstFormat1.hh  |    2 +-
 .../src/OT/Layout/GSUB/SingleSubstFormat2.hh  |    2 +-
 .../harfbuzz-src/src/OT/Layout/types.hh       |    3 +
 .../harfbuzz-src/src/OT/Var/VARC/VARC.cc      |  104 +-
 .../harfbuzz-src/src/OT/Var/VARC/VARC.hh      |  165 +-
 .../src/OT/Var/VARC/coord-setter.hh           |   40 +-
 .../src/OT/glyf/CompositeGlyph.hh             |    2 +-
 .../harfbuzz-src/src/OT/glyf/Glyph.hh         |   66 +-
 .../harfbuzz-src/src/OT/glyf/SimpleGlyph.hh   |   18 +-
 .../harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh |  163 +-
 .../harfbuzz-src/src/OT/glyf/path-builder.hh  |  100 +-
 .../harfbuzz/harfbuzz-src/src/OT/name/name.hh |   10 +-
 .../harfbuzz-src/src/gen-vowel-constraints.py |    2 +-
 .../harfbuzz-src/src/hb-aat-layout-common.hh  |  243 +-
 .../src/hb-aat-layout-kerx-table.hh           |  124 +-
 .../src/hb-aat-layout-morx-table.hh           |  395 +-
 .../src/hb-aat-layout-trak-table.hh           |  184 +-
 .../harfbuzz-src/src/hb-aat-layout.cc         |   46 +-
 .../harfbuzz-src/src/hb-aat-layout.hh         |    8 +-
 .../harfbuzz/harfbuzz-src/src/hb-aat-map.cc   |   15 +-
 .../libs/harfbuzz/harfbuzz-src/src/hb-algs.hh |   10 +-
 .../harfbuzz/harfbuzz-src/src/hb-array.hh     |    3 +-
 .../harfbuzz/harfbuzz-src/src/hb-atomic.hh    |    1 +
 .../harfbuzz/harfbuzz-src/src/hb-bit-page.hh  |   55 +-
 .../harfbuzz-src/src/hb-bit-set-invertible.hh |    4 +
 .../harfbuzz/harfbuzz-src/src/hb-bit-set.hh   |   65 +-
 .../src/hb-buffer-deserialize-json.hh         |  434 +-
 .../src/hb-buffer-deserialize-json.rl         |    7 +-
 .../harfbuzz/harfbuzz-src/src/hb-buffer.cc    |    8 +-
 .../harfbuzz/harfbuzz-src/src/hb-buffer.hh    |   17 +-
 .../harfbuzz-src/src/hb-cff-interp-common.hh  |    2 +-
 .../harfbuzz/harfbuzz-src/src/hb-config.hh    |    1 +
 .../harfbuzz-src/src/hb-coretext-font.cc      |  102 +-
 .../harfbuzz-src/src/hb-coretext-shape.cc     |   48 +-
 .../harfbuzz-src/src/hb-directwrite.cc        |  115 +-
 .../harfbuzz-src/src/hb-directwrite.h         |   16 +-
 .../libs/harfbuzz/harfbuzz-src/src/hb-face.cc |   46 +-
 .../libs/harfbuzz/harfbuzz-src/src/hb-face.h  |    9 +-
 .../harfbuzz/harfbuzz-src/src/hb-ft-colr.hh   |   30 +-
 .../libs/harfbuzz/harfbuzz-src/src/hb-ft.cc   |   77 +-
 source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h |    9 +-
 .../harfbuzz/harfbuzz-src/src/hb-geometry.hh  |    9 +
 .../harfbuzz/harfbuzz-src/src/hb-open-type.hh |  194 +-
 .../harfbuzz-src/src/hb-ot-cff1-table.cc      |    9 -
 .../harfbuzz-src/src/hb-ot-cff1-table.hh      |    1 -
 .../harfbuzz-src/src/hb-ot-cff2-table.cc      |    9 -
 .../harfbuzz-src/src/hb-ot-cff2-table.hh      |    1 -
 .../harfbuzz-src/src/hb-ot-face-table-list.hh |    5 +-
 .../harfbuzz/harfbuzz-src/src/hb-ot-face.cc   |    1 +
 .../harfbuzz/harfbuzz-src/src/hb-ot-font.cc   |   60 +-
 .../harfbuzz-src/src/hb-ot-hdmx-table.hh      |    2 +-
 .../harfbuzz-src/src/hb-ot-hmtx-table.hh      |    2 +-
 .../harfbuzz-src/src/hb-ot-kern-table.hh      |    4 +-
 .../src/hb-ot-layout-base-table.hh            |    2 +-
 .../harfbuzz-src/src/hb-ot-layout-common.hh   |   63 +-
 .../harfbuzz-src/src/hb-ot-layout-gsubgpos.hh |  169 +-
 .../harfbuzz/harfbuzz-src/src/hb-ot-layout.cc |   18 +-
 .../harfbuzz/harfbuzz-src/src/hb-ot-layout.hh |   10 +-
 .../harfbuzz/harfbuzz-src/src/hb-ot-map.cc    |   14 +
 .../harfbuzz/harfbuzz-src/src/hb-ot-map.hh    |    3 +
 .../harfbuzz/harfbuzz-src/src/hb-ot-shape.cc  |   51 +-
 .../harfbuzz/harfbuzz-src/src/hb-ot-shape.h   |    6 +
 .../harfbuzz/harfbuzz-src/src/hb-ot-shape.hh  |   15 +-
 .../src/hb-ot-shaper-arabic-fallback.hh       |    2 +
 .../src/hb-ot-shaper-vowel-constraints.cc     |    2 +-
 .../harfbuzz-src/src/hb-ot-tag-table.hh       |  202 +-
 .../harfbuzz-src/src/hb-ot-var-common.hh      |   40 +-
 .../harfbuzz-src/src/hb-ot-var-cvar-table.hh  |   26 +-
 .../harfbuzz-src/src/hb-ot-var-gvar-table.hh  |  141 +-
 .../harfbuzz/harfbuzz-src/src/hb-serialize.hh |    6 +-
 .../harfbuzz-src/src/hb-set-digest.hh         |  167 +-
 .../libs/harfbuzz/harfbuzz-src/src/hb-set.hh  |    4 +
 .../harfbuzz-src/src/hb-shape-plan.cc         |   10 +-
 .../harfbuzz-src/src/hb-subset-cff-common.hh  |    2 +-
 .../harfbuzz-src/src/hb-subset-cff1.cc        |    2 +-
 .../harfbuzz-src/src/hb-subset-plan.hh        |   17 +-
 .../harfbuzz/harfbuzz-src/src/hb-subset.cc    |    2 +-
 .../harfbuzz/harfbuzz-src/src/hb-vector.hh    |   99 +-
 source/libs/harfbuzz/harfbuzz-src/src/hb.hh   |    5 +-
 .../harfbuzz/harfbuzz-src/src/meson.build     |    2 +
 .../harfbuzz-src/src/test-tuple-varstore.cc   |   16 +-
 source/libs/harfbuzz/version.ac               |    2 +-
 source/libs/libpng/ChangeLog                  |    5 +
 source/libs/libpng/README                     |    4 +-
 source/libs/libpng/TLpatches/ChangeLog        |    4 +
 source/libs/libpng/TLpatches/TL-Changes       |    4 +-
 source/libs/libpng/configure                  |   20 +-
 source/libs/libpng/libpng-src/ANNOUNCE        |   30 +-
 source/libs/libpng/libpng-src/CHANGES         |   12 +
 source/libs/libpng/libpng-src/CMakeLists.txt  |    2 +-
 source/libs/libpng/libpng-src/README          |    2 +-
 .../libpng/libpng-src/ci/ci_verify_cmake.sh   |   11 +-
 .../libpng-src/ci/ci_verify_configure.sh      |    7 +-
 .../libpng-src/ci/ci_verify_makefiles.sh      |   11 +-
 .../libs/libpng/libpng-src/ci/lib/ci.lib.sh   |    9 +
 source/libs/libpng/libpng-src/configure.ac    |    6 +-
 .../libpng-src/contrib/libtests/pngimage.c    |   19 +-
 .../libs/libpng/libpng-src/libpng-manual.txt  |    2 +-
 source/libs/libpng/libpng-src/libpng.3        |    6 +-
 source/libs/libpng/libpng-src/libpngpf.3      |    4 +-
 source/libs/libpng/libpng-src/png.5           |    6 +-
 source/libs/libpng/libpng-src/png.c           | 1133 +---
 source/libs/libpng/libpng-src/png.h           |   14 +-
 source/libs/libpng/libpng-src/pngconf.h       |    2 +-
 source/libs/libpng/libpng-src/pngerror.c      |   26 +-
 source/libs/libpng/libpng-src/pngget.c        |  178 +-
 source/libs/libpng/libpng-src/pnginfo.h       |   30 +-
 source/libs/libpng/libpng-src/pnglibconf.h    |    2 +-
 source/libs/libpng/libpng-src/pngmem.c        |   37 +-
 source/libs/libpng/libpng-src/pngpread.c      |  192 +-
 source/libs/libpng/libpng-src/pngpriv.h       |  408 +-
 source/libs/libpng/libpng-src/pngread.c       |  379 +-
 source/libs/libpng/libpng-src/pngrtran.c      |  337 +-
 source/libs/libpng/libpng-src/pngrutil.c      | 1843 +++---
 source/libs/libpng/libpng-src/pngset.c        |  142 +-
 source/libs/libpng/libpng-src/pngstruct.h     |   96 +-
 source/libs/libpng/libpng-src/pngtest.c       |    4 +-
 source/libs/libpng/libpng-src/pngwrite.c      |   39 +-
 source/libs/libpng/libpng-src/pngwutil.c      |    8 +-
 .../libpng-src/scripts/libpng-config-head.in  |    2 +-
 .../libpng/libpng-src/scripts/libpng.pc.in    |    2 +-
 .../libpng/libpng-src/scripts/pnglibconf.dfa  |   11 +-
 .../libpng-src/scripts/pnglibconf.h.prebuilt  |    2 +-
 source/libs/libpng/version.ac                 |    2 +-
 source/tardate.ac                             |    6 +-
 source/texk/README                            |    4 +-
 source/texk/configure                         |   20 +-
 source/texk/kpathsea/ChangeLog                |    8 +
 source/texk/kpathsea/NEWS                     |    4 +-
 source/texk/kpathsea/c-auto.in                |    2 +-
 source/texk/kpathsea/configure                |   26 +-
 source/texk/kpathsea/version.ac               |    4 +-
 source/texk/texlive/configure                 |   20 +-
 source/texk/web2c/ChangeLog                   |   14 +
 source/texk/web2c/NEWS                        |    2 +-
 source/texk/web2c/configure                   |   24 +-
 source/texk/web2c/cwebdir/ChangeLog           |    8 +
 source/texk/web2c/lib/ChangeLog               |    9 +
 source/texk/web2c/lib/texmfmp.c               |    2 +-
 .../texk/web2c/luatexdir/luatex_svnversion.h  |    2 +-
 source/texk/web2c/man/ChangeLog               |    4 +
 source/texk/web2c/synctexdir/ChangeLog        |    9 +
 source/texk/web2c/synctexdir/synctex.c        |    7 +-
 source/texk/web2c/tangle.ch                   |   19 +-
 source/texk/web2c/tangleboot.pin              |   28 +-
 source/texk/web2c/web2c/ChangeLog             |    4 +
 source/texk/web2c/web2c/configure             |   20 +-
 source/utils/README                           |    7 +-
 source/utils/configure                        |   20 +-
 source/version.ac                             |    4 +-
 177 files changed, 6796 insertions(+), 8575 deletions(-)

diff --git a/source/ChangeLog b/source/ChangeLog
index 0443f2888..32cc7d789 100644
--- a/source/ChangeLog
+++ b/source/ChangeLog
@@ -1,3 +1,11 @@
+2025-03-08  Karl Berry  <karl@tug.org>
+
+	* version.ac: 2026/dev.
+
+2025-03-07  Karl Berry  <karl@tug.org>
+
+	* TL'25 release.
+
 2025-01-12  Karl Berry  <karl@tug.org>
 
 	* Build: also support --debug-more for -g -Og -ggdb3,
diff --git a/source/README b/source/README
index 38acb0274..8b440c126 100644
--- a/source/README
+++ b/source/README
@@ -1,4 +1,4 @@
-$Id: README 73833 2025-02-09 23:41:13Z karl $
+$Id: README 74435 2025-03-04 17:01:16Z karl $
 Public domain.  Originally written 2005 by Karl Berry.
 
 For a high-level overview of building TeX Live, see
@@ -27,30 +27,20 @@ that document).
 Build information for some of the platforms.
 See also Master/tlpkg/bin/tl-update-bindir.
 
-aarch64-linux:
-  Built on contextgarden, see below, except for asy:
-  aarch64 Debian GNU/Linux 10 (buster)
-  gcc (Debian 8.3.0-6) 8.3.0
+aarch64-linux: Built on github, see below;
+  except for asy, by jhielscher on Fedora 41 (glibc 2.40, GCC 14.2.1).
   ./Build --enable-arm-neon=on
 
-armhf-linux:
-  Built on contextgarden, see below.
-  Raspbian/Raspberry Pi OS (Debian Buster)
-  Previously, built by Simon Dales:
-  gcc version 10.2.1 20210110 (Raspbian 10.2.1-6+rpi1)
-  ./Build --enable-xindy CLISP=${BUILD_ROOT_DIR}/clisp/clisp-build/clisp}
-  armhf-linux binaries are created and tested on RPi;
-  they run on RPi, as well as ARMv7 CPUs, but are untested on non-RPi
-  ARMv6 machines.
+armhf-linux: github, see below.
 
 x86_64-cygwin: gcc-10.2.0, cygwin-3.1.7
   TL_CONFIGURE_ARGS="--enable-xindy --enable-shared CLISP=/path/to/clisp.exe
     LDFLAGS='-Wl,--no-insert-timestamp -Wl,--stack,0x800000'" \
   ./Build
   
-i386-freebsd amd64-freebsd: see info at end.
+i386-freebsd amd64-freebsd: github, see below.
 
-i386-linux: see info at end.
+i386-linux: github, see below.
 
 i386-netbsd, amd64-netbsd:
   NetBSD/amd64 10.1
@@ -60,7 +50,7 @@ i386-netbsd, amd64-netbsd:
     LDFLAGS='-L/usr/X11R7/lib -Wl,-rpath,/usr/X11R7/lib' \
   ./Build --enable-xindy CLISP=/usr/local/bin/clisp
 
-i386-solaris, x86_64-solaris: see info at end.
+i386-solaris, x86_64-solaris: github, see below.
 
 universal-darwin: See Master/source/mactexdoc.tar.xz.
 
@@ -68,7 +58,7 @@ windows:
   Makefiles written by hand, see Master/source/windows-src.tar.xz.
   Visual Studio 2010 and Visual Studio 2015.
 
-x86_64-darwinlegacy:
+x86_64-darwinlegacy: Built on ConTeXtGarden.
   Mac OS X 10.6, clang 5.0, libc++ required
   auxiliary installer binaries: Mac OS X 10.6, gcc -std=c99.
   https://github.com/TeXLive-M/texlive-buildbot
@@ -77,6 +67,7 @@ x86_64-darwinlegacy:
 
 x86_64-linux,
 x86_64-linuxmusl,
-and others noted above:
+and others noted above for github:
   built at https://github.com/TeX-Live/texlive-source/releases.
-  see ./github/scripts/build-tl.sh (and workflows/main.yml).
+  see ./github/scripts/build-tl.sh and workflows/main.yml.
+  Some asy binaries are also built here; see */build-asy.*.
diff --git a/source/configure b/source/configure
index 878dd9bc7..5c2735663 100755
--- a/source/configure
+++ b/source/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for TeX Live 2024-03-10.
+# Generated by GNU Autoconf 2.72 for TeX Live 2025-03-07.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -614,8 +614,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='TeX Live'
 PACKAGE_TARNAME='tex-live'
-PACKAGE_VERSION='2024-03-10'
-PACKAGE_STRING='TeX Live 2024-03-10'
+PACKAGE_VERSION='2025-03-07'
+PACKAGE_STRING='TeX Live 2025-03-07'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -813,23 +813,6 @@ enable_native_texlive_build
 enable_multiplatform
 enable_cxx_runtime_hack
 enable_libtool_hack
-enable_autosp
-enable_axodraw2
-enable_devnag
-enable_lacheck
-enable_m_tx
-enable_pmx
-enable_ps2eps
-enable_t1utils
-enable_texdoctk
-enable_tpic2pdftex
-enable_vlna
-enable_xindy
-enable_xindy_rules
-enable_xindy_docs
-with_clisp_runtime
-enable_xml2pmx
-enable_xpdfopen
 enable_web2c
 with_banner_add
 with_editor
@@ -877,88 +860,16 @@ enable_tektronixwin
 enable_unitermwin
 enable_web_progs
 enable_synctex
-enable_afm2pl
-enable_bibtex_x
-enable_bibtex8
-enable_bibtexu
-enable_chktex
-enable_cjkutils
-enable_detex
-enable_dtl
-enable_dvi2tty
-enable_dvidvi
-enable_dviljk
-enable_dviout_util
-enable_dvipdfm_x
-enable_dvipng
-enable_debug
-enable_timing
-with_gs
-enable_dvipos
-enable_dvipsk
-enable_dvisvgm
-enable_gregorio
-enable_gsftopk
-enable_lcdf_typetools
-enable_cfftot1
-enable_mmafm
-enable_mmpfb
-enable_otfinfo
-enable_otftotfm
-enable_t1dotlessj
-enable_t1lint
-enable_t1rawafm
-enable_t1reencode
-enable_t1testpage
-enable_ttftotype42
-enable_updmap
-enable_makeindexk
-enable_makejvf
-enable_mendexk
-enable_musixtnt
-enable_ps2pk
-enable_psutils
-enable_seetexk
-enable_tex4htk
-enable_ttf2pk2
-enable_ttfdump
-enable_upmendex
-enable_xdvik
-with_xdvi_x_toolkit
 enable_texlive
 enable_linked_scripts
 with_system_harfbuzz
-with_system_icu
-with_system_teckit
 with_system_graphite2
 with_system_zziplib
-with_system_mpfi
-with_mpfi_includes
-with_mpfi_libdir
-with_system_mpfr
-with_mpfr_includes
-with_mpfr_libdir
-with_system_gmp
-with_gmp_includes
-with_gmp_libdir
-with_system_cairo
-with_system_pixman
-with_system_gd
-with_gd_includes
-with_gd_libdir
-with_system_potrace
-with_potrace_includes
-with_potrace_libdir
-with_system_freetype2
 with_system_libpng
-with_system_libpaper
-with_libpaper_includes
-with_libpaper_libdir
 with_system_luajit
 with_system_zlib
 with_zlib_includes
 with_zlib_libdir
-with_system_ptexenc
 with_system_kpathsea
 enable_mktexmf_default
 enable_mktexpk_default
@@ -1549,7 +1460,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 TeX Live 2024-03-10 to adapt to many kinds of systems.
+'configure' configures TeX Live 2025-03-07 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1624,7 +1535,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of TeX Live 2024-03-10:";;
+     short | recursive ) echo "Configuration of TeX Live 2025-03-07:";;
    esac
   cat <<\_ACEOF
 
@@ -1641,22 +1552,6 @@ Optional Features:
                           lib/PLATFORM
   --enable-cxx-runtime-hack  link C++ runtime statically
   --enable-libtool-hack   ignore libtool dependency_libs
-  --disable-autosp        do not build the autosp package
-  --disable-axodraw2      do not build the axodraw2 package
-  --disable-devnag        do not build the devnag package
-  --disable-lacheck       do not build the lacheck package
-  --disable-m-tx          do not build the m-tx package
-  --disable-pmx           do not build the pmx package
-  --disable-ps2eps        do not build the ps2eps package
-  --disable-t1utils       do not build the t1utils package
-  --disable-texdoctk      do not build the texdoctk package
-  --disable-tpic2pdftex   do not build the tpic2pdftex package
-  --disable-vlna          do not build the vlna package
-  --enable-xindy          build the xindy package
-  --enable-xindy-rules      build and install make-rules package
-  --enable-xindy-docs       build and install documentation
-  --disable-xml2pmx       do not build the xml2pmx package
-  --disable-xpdfopen      do not build the xpdfopen package
   --disable-web2c         do not build the web2c (TeX & Co.) package
   --enable-auto-core        cause TeX&MF to dump core, given a certain
                             filename
@@ -1704,57 +1599,6 @@ Optional Features:
   --enable-unitermwin         include Uniterm window support
   --disable-web-progs       do not build WEB programs bibtex ... weave
   --disable-synctex         do not build the SyncTeX library and tool
-  --disable-afm2pl        do not build the afm2pl package
-  --disable-bibtex-x      do not build the bibtex-x package
-  --disable-bibtex8         do not build the bibtex8 program
-  --disable-bibtexu         do not build the bibtexu program
-  --disable-chktex        do not build the chktex package
-  --disable-cjkutils      do not build the cjkutils package
-  --disable-detex         do not build the detex package
-  --disable-dtl           do not build the dtl package
-  --disable-dvi2tty       do not build the dvi2tty package
-  --disable-dvidvi        do not build the dvidvi package
-  --disable-dviljk        do not build the dviljk package
-  --disable-dviout-util   do not build the dviout-util package
-  --disable-dvipdfm-x     do not build the dvipdfm-x package
-  --disable-dvipng        do not build the dvipng package
-  --disable-debug           Compile without debug (-d) option
-  --enable-timing           Output execution time of dvipng
-  --disable-dvipos        do not build the dvipos package
-  --disable-dvipsk        do not build the dvipsk package
-  --disable-dvisvgm       do not build the dvisvgm package
-  --disable-gregorio      do not build the gregorio package
-  --disable-gsftopk       do not build the gsftopk package
-  --disable-lcdf-typetools
-                          do not build the lcdf-typetools package
-  --disable-cfftot1         do not build the cfftot1 program
-  --disable-mmafm           do not build the mmafm program
-  --disable-mmpfb           do not build the mmpfb program
-  --disable-otfinfo         do not build the otfinfo program
-  --disable-otftotfm        do not build the otftotfm program
-  --disable-t1dotlessj      do not build the t1dotlessj program
-  --disable-t1lint          do not build the t1lint program
-  --disable-t1rawafm        do not build the t1rawafm program
-  --disable-t1reencode      do not build the t1reencode program
-  --disable-t1testpage      do not build the t1testpage program
-  --disable-ttftotype42     do not build the ttftotype42 program
-  --disable-auto-cfftot1    disable running cfftot1 from otftotfm
-  --disable-auto-t1dotlessj disable running t1dotlessj from otftotfm
-  --disable-auto-ttftotype42
-                            disable running ttftotype42 from otftotfm
-  --disable-auto-updmap     disable running updmap from otftotfm
-  --disable-makeindexk    do not build the makeindexk package
-  --disable-makejvf       do not build the makejvf package
-  --disable-mendexk       do not build the mendexk package
-  --disable-musixtnt      do not build the musixtnt package
-  --disable-ps2pk         do not build the ps2pk package
-  --disable-psutils       do not build the psutils package
-  --disable-seetexk       do not build the seetexk package
-  --disable-tex4htk       do not build the tex4htk package
-  --disable-ttf2pk2       do not build the ttf2pk2 package
-  --disable-ttfdump       do not build the ttfdump package
-  --disable-upmendex      do not build the upmendex package
-  --disable-xdvik         do not build the xdvik package
   --disable-texlive       do not build the texlive (TeX Live scripts) package
   --disable-linked-scripts  do not install the linked scripts
   --disable-mktexmf-default   do not run mktexmf if MF source missing
@@ -1792,68 +1636,24 @@ Optional Features:
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
-  --with-clisp-runtime=PATH
-                          pathname of clisp runtime to install for `xindy',
-                          `default' to derive from clisp, or `system' to use
-                          installed version
   --with-banner-add=STR   add STR to version string appended to banner lines
   --with-editor=CMD       invoke CMD from the `e' option [vi +%d '%s'] or
                           [texworks --position=%d "%s"]
   --with-mf-x-toolkit     use X toolkit for METAFONT
-  --with-gs=/PATH/TO/gs   Hard-wire the location of GhostScript
-  --with-xdvi-x-toolkit=KIT
-                          Use toolkit KIT (xaw/motif/xaw3d/neXtaw) for xdvi
-                          [default: Xaw]
   --with-system-harfbuzz  use installed harfbuzz headers and library (requires
                           pkg-config)
-  --with-system-icu       use installed ICU headers and libraries (requires
-                          pkg-config or icu-config)
-  --with-system-teckit    use installed teckit headers and library (requires
-                          pkg-config)
   --with-system-graphite2 use installed graphite2 headers and library
                           (requires pkg-config)
   --with-system-zziplib   use installed zziplib headers and library (requires
                           pkg-config)
-  --with-system-mpfi      use installed mpfi headers and library
-  --with-mpfi-includes=DIR
-                          mpfi headers installed in DIR
-  --with-mpfi-libdir=DIR  mpfi library installed in DIR
-  --with-system-mpfr      use installed mpfr headers and library
-  --with-mpfr-includes=DIR
-                          mpfr headers installed in DIR
-  --with-mpfr-libdir=DIR  mpfr library installed in DIR
-  --with-system-gmp       use installed gmp headers and library
-  --with-gmp-includes=DIR gmp headers installed in DIR
-  --with-gmp-libdir=DIR   gmp library installed in DIR
-  --with-system-cairo     use installed cairo headers and library (requires
-                          pkg-config)
-  --with-system-pixman    use installed pixman headers and library (requires
-                          pkg-config)
-  --with-system-gd        use installed gd headers and library
-  --with-gd-includes=DIR  gd headers installed in DIR
-  --with-gd-libdir=DIR    gd library installed in DIR
-  --with-system-potrace   use installed potrace headers and library
-  --with-potrace-includes=DIR
-                          potrace headers installed in DIR
-  --with-potrace-libdir=DIR
-                          potrace library installed in DIR
-  --with-system-freetype2 use installed freetype2 headers and library
-                          (requires freetype-config)
   --with-system-libpng    use installed libpng headers and library (requires
                           pkg-config)
-  --with-system-libpaper  use installed libpaper headers and library
-  --with-libpaper-includes=DIR
-                          libpaper headers installed in DIR
-  --with-libpaper-libdir=DIR
-                          libpaper library installed in DIR
   --with-system-luajit    use installed luajit headers and library (requires
                           pkg-config)
   --with-system-zlib      use installed zlib headers and library
   --with-zlib-includes=DIR
                           zlib headers installed in DIR
   --with-zlib-libdir=DIR  zlib library installed in DIR
-  --with-system-ptexenc   use installed ptexenc headers and library (requires
-                          pkg-config)
   --with-system-kpathsea  use installed kpathsea headers and library (requires
                           pkg-config)
   --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
@@ -1945,7 +1745,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-TeX Live configure 2024-03-10
+TeX Live configure 2025-03-07
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -2653,7 +2453,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 TeX Live $as_me 2024-03-10, which was
+It was created by TeX Live $as_me 2025-03-07, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -5265,333 +5065,6 @@ printf "%s\n" "$as_me: $host -> \`--disable-mfluajit-nowin'" >&6;}
 esac ;;
 esac
 
-## utils/autosp/ac/withenable.ac: configure.ac fragment for Tl subdir
-## configure options and TL libraries required for autosp.
-# Check whether --enable-autosp was given.
-if test ${enable_autosp+y}
-then :
-  enableval=$enable_autosp;
-fi
-case $enable_autosp in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_autosp=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-autosp=$enable_autosp'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-autosp=$enable_autosp'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-autosp=$enable_autosp'"
-    ;;
-esac
-
-## utils/axodraw2/ac/withenable.ac: configure.ac fragment for TL subdir
-## configure options and TL libraries for axodraw2.
-# Check whether --enable-axodraw2 was given.
-if test ${enable_axodraw2+y}
-then :
-  enableval=$enable_axodraw2;
-fi
-case $enable_axodraw2 in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_axodraw2=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-axodraw2=$enable_axodraw2'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-axodraw2=$enable_axodraw2'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-axodraw2=$enable_axodraw2'"
-    ;;
-esac
-
-## utils/devnag/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/devnag/
-## configure options and TL libraries required for devnag
-# Check whether --enable-devnag was given.
-if test ${enable_devnag+y}
-then :
-  enableval=$enable_devnag;
-fi
-case $enable_devnag in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_devnag=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-devnag=$enable_devnag'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-devnag=$enable_devnag'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-devnag=$enable_devnag'"
-    ;;
-esac
-
-## utils/lacheck/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/lacheck/
-## configure options and TL libraries required for lacheck
-# Check whether --enable-lacheck was given.
-if test ${enable_lacheck+y}
-then :
-  enableval=$enable_lacheck;
-fi
-case $enable_lacheck in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_lacheck=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-lacheck=$enable_lacheck'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-lacheck=$enable_lacheck'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-lacheck=$enable_lacheck'"
-    ;;
-esac
-
-## utils/m-tx/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/m-tx/
-## configure options and TL libraries required for mtx
-# Check whether --enable-m-tx was given.
-if test ${enable_m_tx+y}
-then :
-  enableval=$enable_m_tx;
-fi
-case $enable_m_tx in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_m_tx=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-m-tx=$enable_m_tx'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-m-tx=$enable_m_tx'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-m-tx=$enable_m_tx'"
-    ;;
-esac
-
-## utils/pmx/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/pmx/
-## configure options and TL libraries required for pmx
-# Check whether --enable-pmx was given.
-if test ${enable_pmx+y}
-then :
-  enableval=$enable_pmx;
-fi
-case $enable_pmx in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_pmx=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-pmx=$enable_pmx'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-pmx=$enable_pmx'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-pmx=$enable_pmx'"
-    ;;
-esac
-
-## utils/ps2eps/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/ps2eps/
-## configure options and TL libraries required for ps2eps
-# Check whether --enable-ps2eps was given.
-if test ${enable_ps2eps+y}
-then :
-  enableval=$enable_ps2eps;
-fi
-case $enable_ps2eps in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_ps2eps=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-ps2eps=$enable_ps2eps'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-ps2eps=$enable_ps2eps'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-ps2eps=$enable_ps2eps'"
-    ;;
-esac
-
-## utils/t1utils/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/t1utils/
-## configure options and TL libraries required for t1utils
-# Check whether --enable-t1utils was given.
-if test ${enable_t1utils+y}
-then :
-  enableval=$enable_t1utils;
-fi
-case $enable_t1utils in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_t1utils=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-t1utils=$enable_t1utils'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-t1utils=$enable_t1utils'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-t1utils=$enable_t1utils'"
-    ;;
-esac
-
-## utils/texdoctk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/texdoctk/
-## configure options and TL libraries required for texdoctk
-# Check whether --enable-texdoctk was given.
-if test ${enable_texdoctk+y}
-then :
-  enableval=$enable_texdoctk;
-fi
-case $enable_texdoctk in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_texdoctk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-texdoctk=$enable_texdoctk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-texdoctk=$enable_texdoctk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-texdoctk=$enable_texdoctk'"
-    ;;
-esac
-
-## utils/tpic2pdftex/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/tpic2pdftex/
-## configure options and TL libraries required for tpic2pdftex
-# Check whether --enable-tpic2pdftex was given.
-if test ${enable_tpic2pdftex+y}
-then :
-  enableval=$enable_tpic2pdftex;
-fi
-case $enable_tpic2pdftex in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_tpic2pdftex=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-tpic2pdftex=$enable_tpic2pdftex'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-tpic2pdftex=$enable_tpic2pdftex'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-tpic2pdftex=$enable_tpic2pdftex'"
-    ;;
-esac
-
-## utils/vlna/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/vlna/
-## configure options and TL libraries required for vlna
-# Check whether --enable-vlna was given.
-if test ${enable_vlna+y}
-then :
-  enableval=$enable_vlna;
-fi
-case $enable_vlna in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_vlna=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-vlna=$enable_vlna'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-vlna=$enable_vlna'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-vlna=$enable_vlna'"
-    ;;
-esac
-
-## utils/xindy/ac/withenable.ac: configure.ac fragment for TL subdir
-## configure options and TL libraries required for xindy.
-# Check whether --enable-xindy was given.
-if test ${enable_xindy+y}
-then :
-  enableval=$enable_xindy;
-fi
-case $enable_xindy in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_xindy=no
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-xindy=$enable_xindy'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-xindy=$enable_xindy'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-xindy=$enable_xindy'"
-    ;;
-esac
-
-## utils/xindy/ac/xindy.ac: configure.ac fragment for the TeX Live subdirectory utils/xindy/
-## configure options for xindy
-# Check whether --enable-xindy-rules was given.
-if test ${enable_xindy_rules+y}
-then :
-  enableval=$enable_xindy_rules;
-fi
-# Check whether --enable-xindy-docs was given.
-if test ${enable_xindy_docs+y}
-then :
-  enableval=$enable_xindy_docs;
-fi
-
-# Check whether --with-clisp-runtime was given.
-if test ${with_clisp_runtime+y}
-then :
-  withval=$with_clisp_runtime;
-fi
-
-## utils/xindy/ac/clisp.ac: configure.ac fragment for the TeX Live subdirectory utils/xindy/
-## configure checks for xindy and clisp
-case $with_clisp_runtime in #(
-  default) :
-     ;; #(
-  system) :
-    if test "x$enable_native_texlive_build" = xyes
-then :
-  as_fn_error $? "you can not use the installed clisp for a native TeX Live build" "$LINENO" 5
-fi ;; #(
-  "") :
-    if test "x$enable_native_texlive_build" = xyes
-then :
-  with_clisp_runtime=default
-else case e in #(
-  e) with_clisp_runtime=system ;;
-esac
-fi
-               { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--with-clisp-runtime=$with_clisp_runtime'" >&5
-printf "%s\n" "$as_me: Assuming \`--with-clisp-runtime=$with_clisp_runtime'" >&6;}
-               ac_configure_args="$ac_configure_args '--with-clisp-runtime=$with_clisp_runtime'" ;; #(
-  *) :
-    if test ! -f "$with_clisp_runtime"
-then :
-  as_fn_error $? "no such file: \"$with_clisp_runtime\"" "$LINENO" 5
-fi ;;
-esac
-
-## utils/xml2pmx/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/xml2pmx/
-## configure options and TL libraries required for xml2pmx
-# Check whether --enable-xml2pmx was given.
-if test ${enable_xml2pmx+y}
-then :
-  enableval=$enable_xml2pmx;
-fi
-case $enable_xml2pmx in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_xml2pmx=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-xml2pmx=$enable_xml2pmx'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-xml2pmx=$enable_xml2pmx'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-xml2pmx=$enable_xml2pmx'"
-    ;;
-esac
-
-## utils/xpdfopen/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory utils/xpdfopen/
-## configure options and TL libraries required for xpdfopen
-# Check whether --enable-xpdfopen was given.
-if test ${enable_xpdfopen+y}
-then :
-  enableval=$enable_xpdfopen;
-fi
-if test "x$with_x" = xno
-then :
-  case $enable_xpdfopen in #(
-  "") :
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \`--without-x' -> \`--disable-xpdfopen'" >&5
-printf "%s\n" "$as_me: \`--without-x' -> \`--disable-xpdfopen'" >&6;}
-                      enable_xpdfopen=no
-                      ac_configure_args="$ac_configure_args '--disable-xpdfopen'" ;; #(
-  yes) :
-    as_fn_error $? "Sorry, incompatible options \`--without-x' and \`--enable-xpdfopen'" "$LINENO" 5 ;; #(
-  *) :
-     ;;
-esac
-fi
-case $enable_xpdfopen in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_xpdfopen=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-xpdfopen=$enable_xpdfopen'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-xpdfopen=$enable_xpdfopen'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-xpdfopen=$enable_xpdfopen'"
-    ;;
-esac
-
 
 ## texk/web2c/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/web2c/
 ## configure options and TL libraries required for web2c
@@ -6106,1839 +5579,1236 @@ then :
 fi
 
 
-## texk/afm2pl/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/afm2pl/
-## configure options and TL libraries required for afm2pl
-# Check whether --enable-afm2pl was given.
-if test ${enable_afm2pl+y}
+## texk/texlive/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/texlive/
+## configure options and TL libraries required for texlive
+# Check whether --enable-texlive was given.
+if test ${enable_texlive+y}
 then :
-  enableval=$enable_afm2pl;
+  enableval=$enable_texlive;
 fi
-case $enable_afm2pl in #(
+case $enable_texlive in #(
   yes|no) :
      ;; #(
   *) :
 
-   enable_afm2pl=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-afm2pl=$enable_afm2pl'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-afm2pl=$enable_afm2pl'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-afm2pl=$enable_afm2pl'"
+   enable_texlive=$enable_all_pkgs
+     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-texlive=$enable_texlive'" >&5
+printf "%s\n" "$as_me: Assuming \`--enable-texlive=$enable_texlive'" >&6;}
+     ac_configure_args="$ac_configure_args '--enable-texlive=$enable_texlive'"
     ;;
 esac
 
-test "x$enable_afm2pl" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/bibtex-x/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/bibtex-x/
-## configure options and TL libraries required for bibtex-x
-# Check whether --enable-bibtex-x was given.
-if test ${enable_bibtex_x+y}
+## texk/texlive/ac/texlive.ac: configure.ac fragment for the TeX Live subdirectory texk/texlive/
+## configure options for texlive
+# Check whether --enable-linked-scripts was given.
+if test ${enable_linked_scripts+y}
 then :
-  enableval=$enable_bibtex_x;
+  enableval=$enable_linked_scripts;
 fi
-case $enable_bibtex_x in #(
-  yes|no) :
-     ;; #(
-  *) :
 
-   enable_bibtex_x=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-bibtex-x=$enable_bibtex_x'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-bibtex-x=$enable_bibtex_x'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-bibtex-x=$enable_bibtex_x'"
-    ;;
-esac
 
-test "x$enable_bibtex_x" = xno || {
-  need_kpathsea=yes
+## libs/pplib/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/pplib/
+## configure options and TL libraries required for pplib
+
+test "x$need_pplib" = xyes && {
+  need_zlib=yes
 }
 
-## texk/bibtex-x/ac/bibtex-x.ac: configure.ac fragment for the TeX Live subdirectory texk/bibtex-x/
-## configure options for bibtex-x
-# Check whether --enable-bibtex8 was given.
-if test ${enable_bibtex8+y}
-then :
-  enableval=$enable_bibtex8;
-fi
+## libs/harfbuzz/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/harfbuzz/
+## configure options and TL libraries required for harfbuzz
 
-case $enable_bibtex8 in #(
-  yes | no) :
-     ;; #(
-  *) :
-    enable_bibtex8=yes ;;
-esac
-# Check whether --enable-bibtexu was given.
-if test ${enable_bibtexu+y}
+# Check whether --with-system-harfbuzz was given.
+if test ${with_system_harfbuzz+y}
 then :
-  enableval=$enable_bibtexu;
+  withval=$with_system_harfbuzz;
+fi
+if test "x$with_system_harfbuzz" = x; then
+  if test -f $srcdir/libs/harfbuzz/configure; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`harfbuzz' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Assuming \`harfbuzz' headers and library from TL tree" >&6;}
+    with_system_harfbuzz=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`harfbuzz' headers and library" >&5
+printf "%s\n" "$as_me: Assuming installed \`harfbuzz' headers and library" >&6;}
+    with_system_harfbuzz=yes
+  fi
+  ac_configure_args="$ac_configure_args '--with-system-harfbuzz=$with_system_harfbuzz'"
+elif test "x$with_system_harfbuzz" = xyes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`harfbuzz' headers and library" >&5
+printf "%s\n" "$as_me: Using installed \`harfbuzz' headers and library" >&6;}
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`harfbuzz' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Using \`harfbuzz' headers and library from TL tree" >&6;}
+  if test "x$with_system_harfbuzz" != xno; then
+    with_system_harfbuzz=no
+    ac_configure_args="$ac_configure_args '--without-system-harfbuzz'"
+  fi
+fi
+if test "x$with_system_harfbuzz" = xyes; then
+  if test "x$with_system_graphite2" = x; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`graphite2' headers and library" >&5
+printf "%s\n" "$as_me:   ->  installed \`graphite2' headers and library" >&6;}
+    with_system_graphite2=yes
+    ac_configure_args="$ac_configure_args '--with-system-graphite2'"
+  elif test "x$with_system_graphite2" != xyes; then
+    as_fn_error $? "Sorry, \`--with-system-harfbuzz' requires \`--with-system-graphite2'" "$LINENO" 5
+  fi
+  if test "x$with_system_icu" = x; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`icu' headers and library" >&5
+printf "%s\n" "$as_me:   ->  installed \`icu' headers and library" >&6;}
+    with_system_icu=yes
+    ac_configure_args="$ac_configure_args '--with-system-icu'"
+  elif test "x$with_system_icu" != xyes; then
+    as_fn_error $? "Sorry, \`--with-system-harfbuzz' requires \`--with-system-icu'" "$LINENO" 5
+  fi
 fi
 
-case $enable_bibtexu in #(
-  yes | no) :
-     ;; #(
-  *) :
-    enable_bibtexu=yes ;;
-esac
+test "x$need_harfbuzz" = xyes && {
+  need_graphite2=yes
+  need_icu=yes
+}
 
-test "x$enable_bibtex_x:$enable_bibtexu" = xyes:yes && need_icu=yes
+## libs/graphite2/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/graphite2/
+## configure options and TL libraries required for graphite2
 
-## texk/chktex/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/chktex/
-## configure options and TL libraries required for chktex
-# Check whether --enable-chktex was given.
-if test ${enable_chktex+y}
+# Check whether --with-system-graphite2 was given.
+if test ${with_system_graphite2+y}
 then :
-  enableval=$enable_chktex;
+  withval=$with_system_graphite2;
 fi
-case $enable_chktex in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_chktex=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-chktex=$enable_chktex'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-chktex=$enable_chktex'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-chktex=$enable_chktex'"
-    ;;
-esac
-
-test "x$enable_chktex" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/cjkutils/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/cjkutils/
-## configure options and TL libraries required for cjkutils
-# Check whether --enable-cjkutils was given.
-if test ${enable_cjkutils+y}
-then :
-  enableval=$enable_cjkutils;
+if test "x$with_system_graphite2" = x; then
+  if test -f $srcdir/libs/graphite2/configure; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`graphite2' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Assuming \`graphite2' headers and library from TL tree" >&6;}
+    with_system_graphite2=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`graphite2' headers and library" >&5
+printf "%s\n" "$as_me: Assuming installed \`graphite2' headers and library" >&6;}
+    with_system_graphite2=yes
+  fi
+  ac_configure_args="$ac_configure_args '--with-system-graphite2=$with_system_graphite2'"
+elif test "x$with_system_graphite2" = xyes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`graphite2' headers and library" >&5
+printf "%s\n" "$as_me: Using installed \`graphite2' headers and library" >&6;}
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`graphite2' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Using \`graphite2' headers and library from TL tree" >&6;}
+  if test "x$with_system_graphite2" != xno; then
+    with_system_graphite2=no
+    ac_configure_args="$ac_configure_args '--without-system-graphite2'"
+  fi
 fi
-case $enable_cjkutils in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_cjkutils=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-cjkutils=$enable_cjkutils'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-cjkutils=$enable_cjkutils'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-cjkutils=$enable_cjkutils'"
-    ;;
-esac
 
-test "x$enable_cjkutils" = xno || {
-  need_kpathsea=yes
-}
+## libs/zziplib/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/zziplib/
+## configure options and TL libraries required for zziplib
 
-## texk/detex/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/detex/
-## configure options and TL libraries required for detex
-# Check whether --enable-detex was given.
-if test ${enable_detex+y}
+# Check whether --with-system-zziplib was given.
+if test ${with_system_zziplib+y}
 then :
-  enableval=$enable_detex;
+  withval=$with_system_zziplib;
 fi
-case $enable_detex in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_detex=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-detex=$enable_detex'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-detex=$enable_detex'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-detex=$enable_detex'"
-    ;;
-esac
-
-test "x$enable_detex" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/dtl/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dtl/
-## configure options and TL libraries required for dtl
-# Check whether --enable-dtl was given.
-if test ${enable_dtl+y}
-then :
-  enableval=$enable_dtl;
+if test "x$with_system_zziplib" = x; then
+  if test -f $srcdir/libs/zziplib/configure; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`zziplib' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Assuming \`zziplib' headers and library from TL tree" >&6;}
+    with_system_zziplib=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`zziplib' headers and library" >&5
+printf "%s\n" "$as_me: Assuming installed \`zziplib' headers and library" >&6;}
+    with_system_zziplib=yes
+  fi
+  ac_configure_args="$ac_configure_args '--with-system-zziplib=$with_system_zziplib'"
+elif test "x$with_system_zziplib" = xyes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`zziplib' headers and library" >&5
+printf "%s\n" "$as_me: Using installed \`zziplib' headers and library" >&6;}
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`zziplib' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Using \`zziplib' headers and library from TL tree" >&6;}
+  if test "x$with_system_zziplib" != xno; then
+    with_system_zziplib=no
+    ac_configure_args="$ac_configure_args '--without-system-zziplib'"
+  fi
 fi
-case $enable_dtl in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dtl=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dtl=$enable_dtl'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dtl=$enable_dtl'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dtl=$enable_dtl'"
-    ;;
-esac
-
-test "x$enable_dtl" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/dvi2tty/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dvi2tty/
-## configure options and TL libraries required for dvi2tty
-# Check whether --enable-dvi2tty was given.
-if test ${enable_dvi2tty+y}
-then :
-  enableval=$enable_dvi2tty;
+if test "x$with_system_zziplib" = xyes; then
+  if test "x$with_system_zlib" = x; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`zlib' headers and library" >&5
+printf "%s\n" "$as_me:   ->  installed \`zlib' headers and library" >&6;}
+    with_system_zlib=yes
+    ac_configure_args="$ac_configure_args '--with-system-zlib'"
+  elif test "x$with_system_zlib" != xyes; then
+    as_fn_error $? "Sorry, \`--with-system-zziplib' requires \`--with-system-zlib'" "$LINENO" 5
+  fi
 fi
-case $enable_dvi2tty in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dvi2tty=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dvi2tty=$enable_dvi2tty'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dvi2tty=$enable_dvi2tty'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dvi2tty=$enable_dvi2tty'"
-    ;;
-esac
 
-test "x$enable_dvi2tty" = xno || {
-  need_ptexenc=yes
+test "x$need_zziplib" = xyes && {
+  need_zlib=yes
 }
 
-## texk/dvidvi/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dvidvi/
-## configure options and TL libraries required for dvidvi
-# Check whether --enable-dvidvi was given.
-if test ${enable_dvidvi+y}
-then :
-  enableval=$enable_dvidvi;
-fi
-case $enable_dvidvi in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dvidvi=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dvidvi=$enable_dvidvi'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dvidvi=$enable_dvidvi'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dvidvi=$enable_dvidvi'"
-    ;;
-esac
-
-test "x$enable_dvidvi" = xno || {
-  need_kpathsea=yes
-}
+## libs/libpng/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/libpng/
+## configure options and TL libraries required for libpng
 
-## texk/dviljk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dviljk/
-## configure options and TL libraries required for dviljk
-# Check whether --enable-dviljk was given.
-if test ${enable_dviljk+y}
+# Check whether --with-system-libpng was given.
+if test ${with_system_libpng+y}
 then :
-  enableval=$enable_dviljk;
+  withval=$with_system_libpng;
 fi
-case $enable_dviljk in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dviljk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dviljk=$enable_dviljk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dviljk=$enable_dviljk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dviljk=$enable_dviljk'"
-    ;;
-esac
-
-test "x$enable_dviljk" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/dviout-util/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dviout-util/
-## configure options and TL libraries required for dviout-util
-# Check whether --enable-dviout-util was given.
-if test ${enable_dviout_util+y}
-then :
-  enableval=$enable_dviout_util;
+if test "x$with_system_libpng" = x; then
+  if test -f $srcdir/libs/libpng/configure; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`libpng' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Assuming \`libpng' headers and library from TL tree" >&6;}
+    with_system_libpng=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`libpng' headers and library" >&5
+printf "%s\n" "$as_me: Assuming installed \`libpng' headers and library" >&6;}
+    with_system_libpng=yes
+  fi
+  ac_configure_args="$ac_configure_args '--with-system-libpng=$with_system_libpng'"
+elif test "x$with_system_libpng" = xyes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`libpng' headers and library" >&5
+printf "%s\n" "$as_me: Using installed \`libpng' headers and library" >&6;}
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`libpng' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Using \`libpng' headers and library from TL tree" >&6;}
+  if test "x$with_system_libpng" != xno; then
+    with_system_libpng=no
+    ac_configure_args="$ac_configure_args '--without-system-libpng'"
+  fi
 fi
-case $enable_dviout_util in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dviout_util=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dviout-util=$enable_dviout_util'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dviout-util=$enable_dviout_util'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dviout-util=$enable_dviout_util'"
-    ;;
-esac
-
-test "x$enable_dviout_util" = xno || {
-  need_ptexenc=yes
-}
-
-## texk/dvipdfm-x/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dvipdfm-x/
-## configure options and TL libraries required for dvipdfm-x
-# Check whether --enable-dvipdfm-x was given.
-if test ${enable_dvipdfm_x+y}
-then :
-  enableval=$enable_dvipdfm_x;
+if test "x$with_system_libpng" = xyes; then
+  if test "x$with_system_zlib" = x; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`zlib' headers and library" >&5
+printf "%s\n" "$as_me:   ->  installed \`zlib' headers and library" >&6;}
+    with_system_zlib=yes
+    ac_configure_args="$ac_configure_args '--with-system-zlib'"
+  elif test "x$with_system_zlib" != xyes; then
+    as_fn_error $? "Sorry, \`--with-system-libpng' requires \`--with-system-zlib'" "$LINENO" 5
+  fi
 fi
-case $enable_dvipdfm_x in #(
-  yes|no) :
-     ;; #(
-  *) :
 
-   enable_dvipdfm_x=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dvipdfm-x=$enable_dvipdfm_x'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dvipdfm-x=$enable_dvipdfm_x'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dvipdfm-x=$enable_dvipdfm_x'"
-    ;;
-esac
-
-test "x$enable_dvipdfm_x" = xno || {
-  need_kpathsea=yes
-  need_libpng=yes
-  need_libpaper=yes
+test "x$need_libpng" = xyes && {
+  need_zlib=yes
 }
 
-## texk/dvipng/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dvipng/
-## configure options and TL libraries required for dvipng
-# Check whether --enable-dvipng was given.
-if test ${enable_dvipng+y}
+## libs/luajit/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/luajit/
+## configure options and TL libraries required for luajit
+
+# Check whether --with-system-luajit was given.
+if test ${with_system_luajit+y}
 then :
-  enableval=$enable_dvipng;
+  withval=$with_system_luajit;
+fi
+if test "x$with_system_luajit" = x; then
+  if test -f $srcdir/libs/luajit/configure; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`luajit' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Assuming \`luajit' headers and library from TL tree" >&6;}
+    with_system_luajit=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`luajit' headers and library" >&5
+printf "%s\n" "$as_me: Assuming installed \`luajit' headers and library" >&6;}
+    with_system_luajit=yes
+  fi
+  ac_configure_args="$ac_configure_args '--with-system-luajit=$with_system_luajit'"
+elif test "x$with_system_luajit" = xyes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`luajit' headers and library" >&5
+printf "%s\n" "$as_me: Using installed \`luajit' headers and library" >&6;}
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`luajit' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Using \`luajit' headers and library from TL tree" >&6;}
+  if test "x$with_system_luajit" != xno; then
+    with_system_luajit=no
+    ac_configure_args="$ac_configure_args '--without-system-luajit'"
+  fi
 fi
-case $enable_dvipng in #(
-  yes|no) :
-     ;; #(
-  *) :
 
-   enable_dvipng=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dvipng=$enable_dvipng'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dvipng=$enable_dvipng'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dvipng=$enable_dvipng'"
-    ;;
-esac
+## libs/lua53/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/lua53/
+## configure options and TL libraries required for lua53
 
-test "x$enable_dvipng" = xno || {
-  need_kpathsea=yes
-  need_gd=yes
-}
+## libs/zlib/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/zlib/
+## configure options and TL libraries required for zlib
 
-## texk/dvipng/ac/dvipng.ac: configure.ac fragment for the TeX Live subdirectory texk/dvipng/
-## configure options for dvipng
-# Check whether --enable-debug was given.
-if test ${enable_debug+y}
+# Check whether --with-system-zlib was given.
+if test ${with_system_zlib+y}
 then :
-  enableval=$enable_debug;
+  withval=$with_system_zlib;
 fi
 
-# Check whether --enable-timing was given.
-if test ${enable_timing+y}
+# Check whether --with-zlib-includes was given.
+if test ${with_zlib_includes+y}
 then :
-  enableval=$enable_timing;
+  withval=$with_zlib_includes;
 fi
 
-
-# Check whether --with-gs was given.
-if test ${with_gs+y}
+# Check whether --with-zlib-libdir was given.
+if test ${with_zlib_libdir+y}
 then :
-  withval=$with_gs;
+  withval=$with_zlib_libdir;
+fi
+if test "x$with_system_zlib" = x; then
+  if test -f $srcdir/libs/zlib/configure; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`zlib' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Assuming \`zlib' headers and library from TL tree" >&6;}
+    with_system_zlib=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`zlib' headers and library" >&5
+printf "%s\n" "$as_me: Assuming installed \`zlib' headers and library" >&6;}
+    with_system_zlib=yes
+  fi
+  ac_configure_args="$ac_configure_args '--with-system-zlib=$with_system_zlib'"
+elif test "x$with_system_zlib" = xyes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`zlib' headers and library" >&5
+printf "%s\n" "$as_me: Using installed \`zlib' headers and library" >&6;}
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`zlib' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Using \`zlib' headers and library from TL tree" >&6;}
+  if test "x$with_system_zlib" != xno; then
+    with_system_zlib=no
+    ac_configure_args="$ac_configure_args '--without-system-zlib'"
+  fi
 fi
 
 
-## texk/dvipos/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dvipos/
-## configure options and TL libraries required for dvipos
-# Check whether --enable-dvipos was given.
-if test ${enable_dvipos+y}
+## texk/kpathsea/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/kpathsea/
+## configure options and TL libraries required for kpathsea
+
+# Check whether --with-system-kpathsea was given.
+if test ${with_system_kpathsea+y}
 then :
-  enableval=$enable_dvipos;
+  withval=$with_system_kpathsea;
 fi
-case $enable_dvipos in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dvipos=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dvipos=$enable_dvipos'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dvipos=$enable_dvipos'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dvipos=$enable_dvipos'"
-    ;;
-esac
-
-test "x$enable_dvipos" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/dvipsk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/dvipsk/
-## configure options and TL libraries required for dvipsk
-# Check whether --enable-dvipsk was given.
-if test ${enable_dvipsk+y}
-then :
-  enableval=$enable_dvipsk;
-fi
-case $enable_dvipsk in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dvipsk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dvipsk=$enable_dvipsk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dvipsk=$enable_dvipsk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dvipsk=$enable_dvipsk'"
-    ;;
-esac
-
-test "x$enable_dvipsk" = xno || {
-  need_kpathsea=yes
-}
-
-# texk/dvisvgm/ac/withenable.ac: configure.ac fragment
-## configure options and TL libraries required for dvisvgm
-# Check whether --enable-dvisvgm was given.
-if test ${enable_dvisvgm+y}
-then :
-  enableval=$enable_dvisvgm;
-fi
-case $enable_dvisvgm in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_dvisvgm=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-dvisvgm=$enable_dvisvgm'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-dvisvgm=$enable_dvisvgm'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-dvisvgm=$enable_dvisvgm'"
-    ;;
-esac
-
-test "x$enable_dvisvgm" = xno || {
-  need_kpathsea=yes
-  need_potrace=yes
-  need_freetype2=yes
-  need_zlib=yes
-}
-
-## texk/gregorio/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/gregorio/
-## configure options and TL libraries required for gregorio
-# Check whether --enable-gregorio was given.
-if test ${enable_gregorio+y}
-then :
-  enableval=$enable_gregorio;
-fi
-case $enable_gregorio in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_gregorio=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-gregorio=$enable_gregorio'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-gregorio=$enable_gregorio'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-gregorio=$enable_gregorio'"
-    ;;
-esac
-
-test "x$enable_gregorio" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/gsftopk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/gsftopk/
-## configure options and TL libraries required for gsftopk
-# Check whether --enable-gsftopk was given.
-if test ${enable_gsftopk+y}
-then :
-  enableval=$enable_gsftopk;
-fi
-case $enable_gsftopk in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_gsftopk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-gsftopk=$enable_gsftopk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-gsftopk=$enable_gsftopk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-gsftopk=$enable_gsftopk'"
-    ;;
-esac
-
-test "x$enable_gsftopk" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/lcdf-typetools/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/lcdf-typetools/
-## configure options and TL libraries required for lcdf-typetools
-# Check whether --enable-lcdf-typetools was given.
-if test ${enable_lcdf_typetools+y}
-then :
-  enableval=$enable_lcdf_typetools;
+if test "x$with_system_kpathsea" = x; then
+  if test -f $srcdir/texk/kpathsea/configure; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`kpathsea' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Assuming \`kpathsea' headers and library from TL tree" >&6;}
+    with_system_kpathsea=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`kpathsea' headers and library" >&5
+printf "%s\n" "$as_me: Assuming installed \`kpathsea' headers and library" >&6;}
+    with_system_kpathsea=yes
+  fi
+  ac_configure_args="$ac_configure_args '--with-system-kpathsea=$with_system_kpathsea'"
+elif test "x$with_system_kpathsea" = xyes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`kpathsea' headers and library" >&5
+printf "%s\n" "$as_me: Using installed \`kpathsea' headers and library" >&6;}
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`kpathsea' headers and library from TL tree" >&5
+printf "%s\n" "$as_me: Using \`kpathsea' headers and library from TL tree" >&6;}
+  if test "x$with_system_kpathsea" != xno; then
+    with_system_kpathsea=no
+    ac_configure_args="$ac_configure_args '--without-system-kpathsea'"
+  fi
 fi
-case $enable_lcdf_typetools in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_lcdf_typetools=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-lcdf-typetools=$enable_lcdf_typetools'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-lcdf-typetools=$enable_lcdf_typetools'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-lcdf-typetools=$enable_lcdf_typetools'"
-    ;;
-esac
-
-test "x$enable_lcdf_typetools" = xno || {
-  need_kpathsea=yes
-}
 
-# Define configure options for lcdf-typetools.  Extracted from configure.ac
-# for ease of building TeX Live.
-#
-#
-# Check whether --enable-cfftot1 was given.
-if test ${enable_cfftot1+y}
-then :
-  enableval=$enable_cfftot1;
-fi
-# Check whether --enable-mmafm was given.
-if test ${enable_mmafm+y}
-then :
-  enableval=$enable_mmafm;
-fi
-# Check whether --enable-mmpfb was given.
-if test ${enable_mmpfb+y}
-then :
-  enableval=$enable_mmpfb;
-fi
-# Check whether --enable-otfinfo was given.
-if test ${enable_otfinfo+y}
-then :
-  enableval=$enable_otfinfo;
-fi
-# Check whether --enable-otftotfm was given.
-if test ${enable_otftotfm+y}
+## texk/kpathsea/ac/mktex.ac: configure.ac fragment for the TeX Live
+## subdirectory texk/kpathsea.
+## configure defaults for mktexfmt & Co.
+# Check whether --enable-mktexmf-default was given.
+if test ${enable_mktexmf_default+y}
 then :
-  enableval=$enable_otftotfm;
+  enableval=$enable_mktexmf_default;
 fi
-# Check whether --enable-t1dotlessj was given.
-if test ${enable_t1dotlessj+y}
+# Check whether --enable-mktexpk-default was given.
+if test ${enable_mktexpk_default+y}
 then :
-  enableval=$enable_t1dotlessj;
+  enableval=$enable_mktexpk_default;
 fi
-# Check whether --enable-t1lint was given.
-if test ${enable_t1lint+y}
+# Check whether --enable-mktextfm-default was given.
+if test ${enable_mktextfm_default+y}
 then :
-  enableval=$enable_t1lint;
+  enableval=$enable_mktextfm_default;
 fi
-# Check whether --enable-t1rawafm was given.
-if test ${enable_t1rawafm+y}
+# Check whether --enable-mkocp-default was given.
+if test ${enable_mkocp_default+y}
 then :
-  enableval=$enable_t1rawafm;
+  enableval=$enable_mkocp_default;
 fi
-# Check whether --enable-t1reencode was given.
-if test ${enable_t1reencode+y}
+# Check whether --enable-mkofm-default was given.
+if test ${enable_mkofm_default+y}
 then :
-  enableval=$enable_t1reencode;
+  enableval=$enable_mkofm_default;
 fi
-# Check whether --enable-t1testpage was given.
-if test ${enable_t1testpage+y}
+# Check whether --enable-mktexfmt-default was given.
+if test ${enable_mktexfmt_default+y}
 then :
-  enableval=$enable_t1testpage;
+  enableval=$enable_mktexfmt_default;
 fi
-# Check whether --enable-ttftotype42 was given.
-if test ${enable_ttftotype42+y}
+# Check whether --enable-mktextex-default was given.
+if test ${enable_mktextex_default+y}
 then :
-  enableval=$enable_ttftotype42;
+  enableval=$enable_mktextex_default;
 fi
 
-#
-# Check whether --enable-cfftot1 was given.
-if test ${enable_cfftot1+y}
-then :
-  enableval=$enable_cfftot1;
-fi
-# Check whether --enable-t1dotlessj was given.
-if test ${enable_t1dotlessj+y}
-then :
-  enableval=$enable_t1dotlessj;
-fi
-# Check whether --enable-ttftotype42 was given.
-if test ${enable_ttftotype42+y}
-then :
-  enableval=$enable_ttftotype42;
-fi
-# Check whether --enable-updmap was given.
-if test ${enable_updmap+y}
-then :
-  enableval=$enable_updmap;
-fi
 
 
-## texk/makeindexk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/makeindexk/
-## configure options and TL libraries required for makeindexk
-# Check whether --enable-makeindexk was given.
-if test ${enable_makeindexk+y}
-then :
-  enableval=$enable_makeindexk;
-fi
-case $enable_makeindexk in #(
-  yes|no) :
-     ;; #(
-  *) :
+# end of kpse_setup macro.
+echo 'tldbg:KPSE_SETUP done (toplevel=)' >&5
 
-   enable_makeindexk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-makeindexk=$enable_makeindexk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-makeindexk=$enable_makeindexk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-makeindexk=$enable_makeindexk'"
-    ;;
-esac
 
-test "x$enable_makeindexk" = xno || {
-  need_kpathsea=yes
-}
+am__api_version='1.17'
 
-## texk/makejvf/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/makejvf/
-## configure options and TL libraries required for makejvf
-# Check whether --enable-makejvf was given.
-if test ${enable_makejvf+y}
-then :
-  enableval=$enable_makejvf;
-fi
-case $enable_makejvf in #(
-  yes|no) :
-     ;; #(
-  *) :
 
-   enable_makejvf=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-makejvf=$enable_makejvf'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-makejvf=$enable_makejvf'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-makejvf=$enable_makejvf'"
+  # Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+printf %s "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test ${ac_cv_path_install+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    # Account for fact that we put trailing slashes in our PATH walk.
+case $as_dir in #((
+  ./ | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    rm -rf conftest.one conftest.two conftest.dir
+	    echo one > conftest.one
+	    echo two > conftest.two
+	    mkdir conftest.dir
+	    if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" &&
+	      test -s conftest.one && test -s conftest.two &&
+	      test -s conftest.dir/conftest.one &&
+	      test -s conftest.dir/conftest.two
+	    then
+	      ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c"
+	      break 3
+	    fi
+	  fi
+	fi
+      done
+    done
     ;;
 esac
 
-test "x$enable_makejvf" = xno || {
-  need_ptexenc=yes
-}
+  done
+IFS=$as_save_IFS
 
-## texk/mendexk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/mendexk/
-## configure options and TL libraries required for mendexk
-# Check whether --enable-mendexk was given.
-if test ${enable_mendexk+y}
-then :
-  enableval=$enable_mendexk;
+rm -rf conftest.one conftest.two conftest.dir
+ ;;
+esac
 fi
-case $enable_mendexk in #(
-  yes|no) :
-     ;; #(
-  *) :
+  if test ${ac_cv_path_install+y}; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+printf "%s\n" "$INSTALL" >&6; }
 
-   enable_mendexk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-mendexk=$enable_mendexk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-mendexk=$enable_mendexk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-mendexk=$enable_mendexk'"
-    ;;
-esac
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
 
-test "x$enable_mendexk" = xno || {
-  need_ptexenc=yes
-}
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
-## texk/musixtnt/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/musixtnt/
-## configure options and TL libraries required for musixtnt
-# Check whether --enable-musixtnt was given.
-if test ${enable_musixtnt+y}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether sleep supports fractional seconds" >&5
+printf %s "checking whether sleep supports fractional seconds... " >&6; }
+if test ${am_cv_sleep_fractional_seconds+y}
 then :
-  enableval=$enable_musixtnt;
-fi
-case $enable_musixtnt in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_musixtnt=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-musixtnt=$enable_musixtnt'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-musixtnt=$enable_musixtnt'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-musixtnt=$enable_musixtnt'"
-    ;;
-esac
-
-test "x$enable_musixtnt" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/ps2pk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/ps2pk/
-## configure options and TL libraries required for ps2pk
-# Check whether --enable-ps2pk was given.
-if test ${enable_ps2pk+y}
+  printf %s "(cached) " >&6
+else case e in #(
+  e) if sleep 0.001 2>/dev/null
 then :
-  enableval=$enable_ps2pk;
+  am_cv_sleep_fractional_seconds=yes
+else case e in #(
+  e) am_cv_sleep_fractional_seconds=no ;;
+esac
 fi
-case $enable_ps2pk in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_ps2pk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-ps2pk=$enable_ps2pk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-ps2pk=$enable_ps2pk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-ps2pk=$enable_ps2pk'"
-    ;;
+ ;;
 esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_sleep_fractional_seconds" >&5
+printf "%s\n" "$am_cv_sleep_fractional_seconds" >&6; }
 
-test "x$enable_ps2pk" = xno || {
-  need_kpathsea=yes
-}
-
-## texk/psutils/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/psutils/
-## configure options and TL libraries required for psutils
-# Check whether --enable-psutils was given.
-if test ${enable_psutils+y}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking filesystem timestamp resolution" >&5
+printf %s "checking filesystem timestamp resolution... " >&6; }
+if test ${am_cv_filesystem_timestamp_resolution+y}
 then :
-  enableval=$enable_psutils;
-fi
-case $enable_psutils in #(
-  yes|no) :
-     ;; #(
-  *) :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) # Default to the worst case.
+am_cv_filesystem_timestamp_resolution=2
 
-   enable_psutils=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-psutils=$enable_psutils'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-psutils=$enable_psutils'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-psutils=$enable_psutils'"
-    ;;
-esac
+# Only try to go finer than 1 sec if sleep can do it.
+# Don't try 1 sec, because if 0.01 sec and 0.1 sec don't work,
+# - 1 sec is not much of a win compared to 2 sec, and
+# - it takes 2 seconds to perform the test whether 1 sec works.
+#
+# Instead, just use the default 2s on platforms that have 1s resolution,
+# accept the extra 1s delay when using $sleep in the Automake tests, in
+# exchange for not incurring the 2s delay for running the test for all
+# packages.
+#
+am_try_resolutions=
+if test "$am_cv_sleep_fractional_seconds" = yes; then
+  # Even a millisecond often causes a bunch of false positives,
+  # so just try a hundredth of a second. The time saved between .001 and
+  # .01 is not terribly consequential.
+  am_try_resolutions="0.01 0.1 $am_try_resolutions"
+fi
 
-test "x$enable_psutils" = xno || {
-  need_kpathsea=yes
-  need_libpaper=yes
-}
+# In order to catch current-generation FAT out, we must *modify* files
+# that already exist; the *creation* timestamp is finer.  Use names
+# that make ls -t sort them differently when they have equal
+# timestamps than when they have distinct timestamps, keeping
+# in mind that ls -t prints the *newest* file first.
+rm -f conftest.ts?
+: > conftest.ts1
+: > conftest.ts2
+: > conftest.ts3
 
-## texk/seetexk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/seetexk/
-## configure options and TL libraries required for seetexk
-# Check whether --enable-seetexk was given.
-if test ${enable_seetexk+y}
-then :
-  enableval=$enable_seetexk;
+# Make sure ls -t actually works.  Do 'set' in a subshell so we don't
+# clobber the current shell's arguments. (Outer-level square brackets
+# are removed by m4; they're present so that m4 does not expand
+# <dollar><star>; be careful, easy to get confused.)
+if (
+     set X `ls -t conftest.ts[12]` &&
+     {
+       test "$*" != "X conftest.ts1 conftest.ts2" ||
+       test "$*" != "X conftest.ts2 conftest.ts1";
+     }
+); then :; else
+  # If neither matched, then we have a broken ls.  This can happen
+  # if, for instance, CONFIG_SHELL is bash and it inherits a
+  # broken ls alias from the environment.  This has actually
+  # happened.  Such a system could not be considered "sane".
+  printf "%s\n" ""Bad output from ls -t: \"`ls -t conftest.ts[12]`\""" >&5
+  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+as_fn_error $? "ls -t produces unexpected output.
+Make sure there is not a broken ls alias in your environment.
+See 'config.log' for more details" "$LINENO" 5; }
 fi
-case $enable_seetexk in #(
-  yes|no) :
-     ;; #(
-  *) :
 
-   enable_seetexk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-seetexk=$enable_seetexk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-seetexk=$enable_seetexk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-seetexk=$enable_seetexk'"
-    ;;
-esac
-
-test "x$enable_seetexk" = xno || {
-  need_kpathsea=yes
-}
+for am_try_res in $am_try_resolutions; do
+  # Any one fine-grained sleep might happen to cross the boundary
+  # between two values of a coarser actual resolution, but if we do
+  # two fine-grained sleeps in a row, at least one of them will fall
+  # entirely within a coarse interval.
+  echo alpha > conftest.ts1
+  sleep $am_try_res
+  echo beta > conftest.ts2
+  sleep $am_try_res
+  echo gamma > conftest.ts3
 
-## texk/tex4htk/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/tex4htk/
-## configure options and TL libraries required for tex4htk
-# Check whether --enable-tex4htk was given.
-if test ${enable_tex4htk+y}
-then :
-  enableval=$enable_tex4htk;
+  # We assume that 'ls -t' will make use of high-resolution
+  # timestamps if the operating system supports them at all.
+  if (set X `ls -t conftest.ts?` &&
+      test "$2" = conftest.ts3 &&
+      test "$3" = conftest.ts2 &&
+      test "$4" = conftest.ts1); then
+    #
+    # Ok, ls -t worked. If we're at a resolution of 1 second, we're done,
+    # because we don't need to test make.
+    make_ok=true
+    if test $am_try_res != 1; then
+      # But if we've succeeded so far with a subsecond resolution, we
+      # have one more thing to check: make. It can happen that
+      # everything else supports the subsecond mtimes, but make doesn't;
+      # notably on macOS, which ships make 3.81 from 2006 (the last one
+      # released under GPLv2). https://bugs.gnu.org/68808
+      #
+      # We test $MAKE if it is defined in the environment, else "make".
+      # It might get overridden later, but our hope is that in practice
+      # it does not matter: it is the system "make" which is (by far)
+      # the most likely to be broken, whereas if the user overrides it,
+      # probably they did so with a better, or at least not worse, make.
+      # https://lists.gnu.org/archive/html/automake/2024-06/msg00051.html
+      #
+      # Create a Makefile (real tab character here):
+      rm -f conftest.mk
+      echo 'conftest.ts1: conftest.ts2' >conftest.mk
+      echo '	touch conftest.ts2' >>conftest.mk
+      #
+      # Now, running
+      #   touch conftest.ts1; touch conftest.ts2; make
+      # should touch ts1 because ts2 is newer. This could happen by luck,
+      # but most often, it will fail if make's support is insufficient. So
+      # test for several consecutive successes.
+      #
+      # (We reuse conftest.ts[12] because we still want to modify existing
+      # files, not create new ones, per above.)
+      n=0
+      make=${MAKE-make}
+      until test $n -eq 3; do
+        echo one > conftest.ts1
+        sleep $am_try_res
+        echo two > conftest.ts2 # ts2 should now be newer than ts1
+        if $make -f conftest.mk | grep 'up to date' >/dev/null; then
+          make_ok=false
+          break # out of $n loop
+        fi
+        n=`expr $n + 1`
+      done
+    fi
+    #
+    if $make_ok; then
+      # Everything we know to check worked out, so call this resolution good.
+      am_cv_filesystem_timestamp_resolution=$am_try_res
+      break # out of $am_try_res loop
+    fi
+    # Otherwise, we'll go on to check the next resolution.
+  fi
+done
+rm -f conftest.ts?
+# (end _am_filesystem_timestamp_resolution)
+ ;;
+esac
 fi
-case $enable_tex4htk in #(
-  yes|no) :
-     ;; #(
-  *) :
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_filesystem_timestamp_resolution" >&5
+printf "%s\n" "$am_cv_filesystem_timestamp_resolution" >&6; }
 
-   enable_tex4htk=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-tex4htk=$enable_tex4htk'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-tex4htk=$enable_tex4htk'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-tex4htk=$enable_tex4htk'"
-    ;;
+# This check should not be cached, as it may vary across builds of
+# different projects.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+printf %s "checking whether build environment is sane... " >&6; }
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[\\\"\#\$\&\'\`$am_lf]*)
+    as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+  *[\\\"\#\$\&\'\`$am_lf\ \	]*)
+    as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
 esac
 
-test "x$enable_tex4htk" = xno || {
-  need_kpathsea=yes
-}
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+am_build_env_is_sane=no
+am_has_slept=no
+rm -f conftest.file
+for am_try in 1 2; do
+  echo "timestamp, slept: $am_has_slept" > conftest.file
+  if (
+    set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+    if test "$*" = "X"; then
+      # -L didn't work.
+      set X `ls -t "$srcdir/configure" conftest.file`
+    fi
+    test "$2" = conftest.file
+  ); then
+    am_build_env_is_sane=yes
+    break
+  fi
+  # Just in case.
+  sleep "$am_cv_filesystem_timestamp_resolution"
+  am_has_slept=yes
+done
 
-## texk/ttf2pk2/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/ttf2pk2/
-## configure options and TL libraries required for ttf2pk
-# Check whether --enable-ttf2pk2 was given.
-if test ${enable_ttf2pk2+y}
-then :
-  enableval=$enable_ttf2pk2;
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_build_env_is_sane" >&5
+printf "%s\n" "$am_build_env_is_sane" >&6; }
+if test "$am_build_env_is_sane" = no; then
+  as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
 fi
-case $enable_ttf2pk2 in #(
-  yes|no) :
-     ;; #(
-  *) :
 
-   enable_ttf2pk2=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-ttf2pk2=$enable_ttf2pk2'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-ttf2pk2=$enable_ttf2pk2'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-ttf2pk2=$enable_ttf2pk2'"
-    ;;
-esac
-
-test "x$enable_ttf2pk2" = xno || {
-  need_kpathsea=yes
-  need_freetype2=yes
-}
-
-## texk/ttfdump/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/ttfdump/
-## configure options and TL libraries required for ttfdump
-# Check whether --enable-ttfdump was given.
-if test ${enable_ttfdump+y}
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if test -e conftest.file || grep 'slept: no' conftest.file >/dev/null 2>&1
 then :
-  enableval=$enable_ttfdump;
-fi
-case $enable_ttfdump in #(
-  yes|no) :
-     ;; #(
-  *) :
 
-   enable_ttfdump=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-ttfdump=$enable_ttfdump'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-ttfdump=$enable_ttfdump'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-ttfdump=$enable_ttfdump'"
-    ;;
+else case e in #(
+  e)   ( sleep "$am_cv_filesystem_timestamp_resolution" ) &
+  am_sleep_pid=$!
+ ;;
 esac
+fi
 
-test "x$enable_ttfdump" = xno || {
-  need_kpathsea=yes
-}
+rm -f conftest.file
 
-## texk/upmendex/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/upmendex/
-## configure options and TL libraries required for upmendex
-# Check whether --enable-upmendex was given.
-if test ${enable_upmendex+y}
-then :
-  enableval=$enable_upmendex;
-fi
-case $enable_upmendex in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_upmendex=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-upmendex=$enable_upmendex'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-upmendex=$enable_upmendex'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-upmendex=$enable_upmendex'"
-    ;;
-esac
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was 's,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"`
 
-test "x$enable_upmendex" = xno || {
-  need_kpathsea=yes
-  need_icu=yes
-}
 
-## texk/xdvik/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/xdvik/
-## configure options and TL libraries required for xdvik
-# Check whether --enable-xdvik was given.
-if test ${enable_xdvik+y}
-then :
-  enableval=$enable_xdvik;
+  if test x"${MISSING+set}" != xset; then
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
 fi
-if test "x$with_x" = xno
-then :
-  case $enable_xdvik in #(
-  "") :
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \`--without-x' -> \`--disable-xdvik'" >&5
-printf "%s\n" "$as_me: \`--without-x' -> \`--disable-xdvik'" >&6;}
-                      enable_xdvik=no
-                      ac_configure_args="$ac_configure_args '--disable-xdvik'" ;; #(
-  yes) :
-    as_fn_error $? "Sorry, incompatible options \`--without-x' and \`--enable-xdvik'" "$LINENO" 5 ;; #(
-  *) :
-     ;;
-esac
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
 fi
-case $enable_xdvik in #(
-  yes|no) :
-     ;; #(
-  *) :
-
-   enable_xdvik=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-xdvik=$enable_xdvik'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-xdvik=$enable_xdvik'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-xdvik=$enable_xdvik'"
-    ;;
-esac
-
-test "x$enable_xdvik" = xno || {
-  need_kpathsea=yes
-  need_freetype2=yes
-}
-
-## texk/xdvik/ac/xdvik.ac: configure.ac fragment for the TeX Live subdirectory texk/xdvik/
-## configure options for xdvik
 
-# Check whether --with-xdvi-x-toolkit was given.
-if test ${with_xdvi_x_toolkit+y}
-then :
-  withval=$with_xdvi_x_toolkit;
+if test x"${install_sh+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
 fi
 
-
-## texk/texlive/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/texlive/
-## configure options and TL libraries required for texlive
-# Check whether --enable-texlive was given.
-if test ${enable_texlive+y}
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_STRIP+y}
 then :
-  enableval=$enable_texlive;
-fi
-case $enable_texlive in #(
-  yes|no) :
-     ;; #(
-  *) :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
-   enable_texlive=$enable_all_pkgs
-     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`--enable-texlive=$enable_texlive'" >&5
-printf "%s\n" "$as_me: Assuming \`--enable-texlive=$enable_texlive'" >&6;}
-     ac_configure_args="$ac_configure_args '--enable-texlive=$enable_texlive'"
-    ;;
+fi ;;
 esac
-
-## texk/texlive/ac/texlive.ac: configure.ac fragment for the TeX Live subdirectory texk/texlive/
-## configure options for texlive
-# Check whether --enable-linked-scripts was given.
-if test ${enable_linked_scripts+y}
-then :
-  enableval=$enable_linked_scripts;
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+printf "%s\n" "$STRIP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
 fi
 
 
-## libs/pplib/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/pplib/
-## configure options and TL libraries required for pplib
-
-test "x$need_pplib" = xyes && {
-  need_zlib=yes
-}
-
-## libs/harfbuzz/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/harfbuzz/
-## configure options and TL libraries required for harfbuzz
-
-# Check whether --with-system-harfbuzz was given.
-if test ${with_system_harfbuzz+y}
-then :
-  withval=$with_system_harfbuzz;
 fi
-if test "x$with_system_harfbuzz" = x; then
-  if test -f $srcdir/libs/harfbuzz/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`harfbuzz' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`harfbuzz' headers and library from TL tree" >&6;}
-    with_system_harfbuzz=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`harfbuzz' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`harfbuzz' headers and library" >&6;}
-    with_system_harfbuzz=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-harfbuzz=$with_system_harfbuzz'"
-elif test "x$with_system_harfbuzz" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`harfbuzz' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`harfbuzz' headers and library" >&6;}
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_STRIP+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`harfbuzz' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`harfbuzz' headers and library from TL tree" >&6;}
-  if test "x$with_system_harfbuzz" != xno; then
-    with_system_harfbuzz=no
-    ac_configure_args="$ac_configure_args '--without-system-harfbuzz'"
-  fi
-fi
-if test "x$with_system_harfbuzz" = xyes; then
-  if test "x$with_system_graphite2" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`graphite2' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`graphite2' headers and library" >&6;}
-    with_system_graphite2=yes
-    ac_configure_args="$ac_configure_args '--with-system-graphite2'"
-  elif test "x$with_system_graphite2" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-harfbuzz' requires \`--with-system-graphite2'" "$LINENO" 5
-  fi
-  if test "x$with_system_icu" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`icu' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`icu' headers and library" >&6;}
-    with_system_icu=yes
-    ac_configure_args="$ac_configure_args '--with-system-icu'"
-  elif test "x$with_system_icu" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-harfbuzz' requires \`--with-system-icu'" "$LINENO" 5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
   fi
-fi
-
-test "x$need_harfbuzz" = xyes && {
-  need_graphite2=yes
-  need_icu=yes
-}
-
-## libs/icu/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/icu/
-## configure options and TL libraries required for icu (modified for XeTeX)
+done
+  done
+IFS=$as_save_IFS
 
-# Check whether --with-system-icu was given.
-if test ${with_system_icu+y}
-then :
-  withval=$with_system_icu;
+fi ;;
+esac
 fi
-if test "x$with_system_icu" = x; then
-  if test -f $srcdir/libs/icu/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`icu' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`icu' headers and library from TL tree" >&6;}
-    with_system_icu=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`icu' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`icu' headers and library" >&6;}
-    with_system_icu=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-icu=$with_system_icu'"
-elif test "x$with_system_icu" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`icu' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`icu' headers and library" >&6;}
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+printf "%s\n" "$ac_ct_STRIP" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`icu' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`icu' headers and library from TL tree" >&6;}
-  if test "x$with_system_icu" != xno; then
-    with_system_icu=no
-    ac_configure_args="$ac_configure_args '--without-system-icu'"
-  fi
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
 fi
 
-## libs/teckit/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/teckit/
-## configure options and TL libraries required for teckit
-
-# Check whether --with-system-teckit was given.
-if test ${with_system_teckit+y}
-then :
-  withval=$with_system_teckit;
-fi
-if test "x$with_system_teckit" = x; then
-  if test -f $srcdir/libs/teckit/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`teckit' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`teckit' headers and library from TL tree" >&6;}
-    with_system_teckit=no
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
   else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`teckit' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`teckit' headers and library" >&6;}
-    with_system_teckit=yes
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
   fi
-  ac_configure_args="$ac_configure_args '--with-system-teckit=$with_system_teckit'"
-elif test "x$with_system_teckit" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`teckit' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`teckit' headers and library" >&6;}
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`teckit' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`teckit' headers and library from TL tree" >&6;}
-  if test "x$with_system_teckit" != xno; then
-    with_system_teckit=no
-    ac_configure_args="$ac_configure_args '--without-system-teckit'"
-  fi
-fi
-if test "x$with_system_teckit" = xyes; then
-  if test "x$with_system_zlib" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`zlib' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`zlib' headers and library" >&6;}
-    with_system_zlib=yes
-    ac_configure_args="$ac_configure_args '--with-system-zlib'"
-  elif test "x$with_system_zlib" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-teckit' requires \`--with-system-zlib'" "$LINENO" 5
-  fi
+  STRIP="$ac_cv_prog_STRIP"
 fi
 
-test "x$need_teckit" = xyes && {
-  need_zlib=yes
-}
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 
-## libs/graphite2/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/graphite2/
-## configure options and TL libraries required for graphite2
 
-# Check whether --with-system-graphite2 was given.
-if test ${with_system_graphite2+y}
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5
+printf %s "checking for a race-free mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if test ${ac_cv_path_mkdir+y}
 then :
-  withval=$with_system_graphite2;
-fi
-if test "x$with_system_graphite2" = x; then
-  if test -f $srcdir/libs/graphite2/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`graphite2' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`graphite2' headers and library from TL tree" >&6;}
-    with_system_graphite2=no
+  printf %s "(cached) " >&6
+else case e in #(
+  e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in mkdir gmkdir; do
+	 for ac_exec_ext in '' $ac_executable_extensions; do
+	   as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue
+	   case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #(
+	     'mkdir ('*'coreutils) '* | \
+	     *'BusyBox '* | \
+	     'mkdir (fileutils) '4.1*)
+	       ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext
+	       break 3;;
+	   esac
+	 done
+       done
+  done
+IFS=$as_save_IFS
+ ;;
+esac
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test ${ac_cv_path_mkdir+y}; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
   else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`graphite2' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`graphite2' headers and library" >&6;}
-    with_system_graphite2=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-graphite2=$with_system_graphite2'"
-elif test "x$with_system_graphite2" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`graphite2' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`graphite2' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`graphite2' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`graphite2' headers and library from TL tree" >&6;}
-  if test "x$with_system_graphite2" != xno; then
-    with_system_graphite2=no
-    ac_configure_args="$ac_configure_args '--without-system-graphite2'"
+    # As a last resort, use plain mkdir -p,
+    # in the hope it doesn't have the bugs of ancient mkdir.
+    MKDIR_P='mkdir -p'
   fi
 fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+printf "%s\n" "$MKDIR_P" >&6; }
 
-## libs/zziplib/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/zziplib/
-## configure options and TL libraries required for zziplib
-
-# Check whether --with-system-zziplib was given.
-if test ${with_system_zziplib+y}
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_AWK+y}
 then :
-  withval=$with_system_zziplib;
-fi
-if test "x$with_system_zziplib" = x; then
-  if test -f $srcdir/libs/zziplib/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`zziplib' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`zziplib' headers and library from TL tree" >&6;}
-    with_system_zziplib=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`zziplib' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`zziplib' headers and library" >&6;}
-    with_system_zziplib=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-zziplib=$with_system_zziplib'"
-elif test "x$with_system_zziplib" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`zziplib' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`zziplib' headers and library" >&6;}
+  printf %s "(cached) " >&6
+else case e in #(
+  e) if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`zziplib' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`zziplib' headers and library from TL tree" >&6;}
-  if test "x$with_system_zziplib" != xno; then
-    with_system_zziplib=no
-    ac_configure_args="$ac_configure_args '--without-system-zziplib'"
-  fi
-fi
-if test "x$with_system_zziplib" = xyes; then
-  if test "x$with_system_zlib" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`zlib' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`zlib' headers and library" >&6;}
-    with_system_zlib=yes
-    ac_configure_args="$ac_configure_args '--with-system-zlib'"
-  elif test "x$with_system_zlib" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-zziplib' requires \`--with-system-zlib'" "$LINENO" 5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
   fi
-fi
-
-test "x$need_zziplib" = xyes && {
-  need_zlib=yes
-}
+done
+  done
+IFS=$as_save_IFS
 
-## libs/xpdf/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/xpdf/
-## configure options and TL libraries required for xpdf
-: "kpse_xpdf_options - no-op"
-if test "x$with_system_xpdf" = x; then
-  if test -f $srcdir/libs/xpdf/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`xpdf' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`xpdf' headers and library from TL tree" >&6;}
-    with_system_xpdf=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`xpdf' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`xpdf' headers and library" >&6;}
-    with_system_xpdf=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-xpdf=$with_system_xpdf'"
-elif test "x$with_system_xpdf" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`xpdf' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`xpdf' headers and library" >&6;}
+fi ;;
+esac
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+printf "%s\n" "$AWK" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`xpdf' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`xpdf' headers and library from TL tree" >&6;}
-  if test "x$with_system_xpdf" != xno; then
-    with_system_xpdf=no
-    ac_configure_args="$ac_configure_args '--without-system-xpdf'"
-  fi
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
 fi
 
-## libs/mpfi/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/mpfi/
-## configure options and TL libraries required for mpfi
-
-# Check whether --with-system-mpfi was given.
-if test ${with_system_mpfi+y}
-then :
-  withval=$with_system_mpfi;
-fi
 
-# Check whether --with-mpfi-includes was given.
-if test ${with_mpfi_includes+y}
-then :
-  withval=$with_mpfi_includes;
-fi
+  test -n "$AWK" && break
+done
 
-# Check whether --with-mpfi-libdir was given.
-if test ${with_mpfi_libdir+y}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval test \${ac_cv_prog_make_${ac_make}_set+y}
 then :
-  withval=$with_mpfi_libdir;
+  printf %s "(cached) " >&6
+else case e in #(
+  e) cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+	@echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make ;;
+esac
 fi
-if test "x$with_system_mpfi" = x; then
-  if test -f $srcdir/libs/mpfi/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`mpfi' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`mpfi' headers and library from TL tree" >&6;}
-    with_system_mpfi=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`mpfi' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`mpfi' headers and library" >&6;}
-    with_system_mpfi=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-mpfi=$with_system_mpfi'"
-elif test "x$with_system_mpfi" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`mpfi' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`mpfi' headers and library" >&6;}
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+  SET_MAKE=
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`mpfi' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`mpfi' headers and library from TL tree" >&6;}
-  if test "x$with_system_mpfi" != xno; then
-    with_system_mpfi=no
-    ac_configure_args="$ac_configure_args '--without-system-mpfi'"
-  fi
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
 fi
-if test "x$with_system_mpfi" = xyes; then
-  if test "x$with_system_mpfr" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`mpfr' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`mpfr' headers and library" >&6;}
-    with_system_mpfr=yes
-    ac_configure_args="$ac_configure_args '--with-system-mpfr'"
-  elif test "x$with_system_mpfr" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-mpfi' requires \`--with-system-mpfr'" "$LINENO" 5
-  fi
-  if test "x$with_system_gmp" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`gmp' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`gmp' headers and library" >&6;}
-    with_system_gmp=yes
-    ac_configure_args="$ac_configure_args '--with-system-gmp'"
-  elif test "x$with_system_gmp" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-mpfi' requires \`--with-system-gmp'" "$LINENO" 5
-  fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
 fi
+rmdir .tst 2>/dev/null
 
-test "x$need_mpfi" = xyes && {
-  need_mpfr=yes
-  need_gmp=yes
-}
+DEPDIR="${am__leading_dot}deps"
 
-## libs/mpfr/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/mpfr/
-## configure options and TL libraries required for mpfr
+ac_config_commands="$ac_config_commands depfiles"
 
-# Check whether --with-system-mpfr was given.
-if test ${with_system_mpfr+y}
-then :
-  withval=$with_system_mpfr;
-fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
+printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; }
+cat > confinc.mk << 'END'
+am__doit:
+	@echo this is the am__doit target >confinc.out
+.PHONY: am__doit
+END
+am__include="#"
+am__quote=
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+  { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
+   (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }
+  case $?:`cat confinc.out 2>/dev/null` in #(
+  '0:this is the am__doit target') :
+    case $s in #(
+  BSD) :
+    am__include='.include' am__quote='"' ;; #(
+  *) :
+    am__include='include' am__quote='' ;;
+esac ;; #(
+  *) :
+     ;;
+esac
+  if test "$am__include" != "#"; then
+    _am_result="yes ($s style)"
+    break
+  fi
+done
+rm -f confinc.* confmf.*
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
+printf "%s\n" "${_am_result}" >&6; }
 
-# Check whether --with-mpfr-includes was given.
-if test ${with_mpfr_includes+y}
+# Check whether --enable-dependency-tracking was given.
+if test ${enable_dependency_tracking+y}
 then :
-  withval=$with_mpfr_includes;
+  enableval=$enable_dependency_tracking;
 fi
 
-# Check whether --with-mpfr-libdir was given.
-if test ${with_mpfr_libdir+y}
-then :
-  withval=$with_mpfr_libdir;
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
 fi
-if test "x$with_system_mpfr" = x; then
-  if test -f $srcdir/libs/mpfr/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`mpfr' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`mpfr' headers and library from TL tree" >&6;}
-    with_system_mpfr=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`mpfr' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`mpfr' headers and library" >&6;}
-    with_system_mpfr=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-mpfr=$with_system_mpfr'"
-elif test "x$with_system_mpfr" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`mpfr' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`mpfr' headers and library" >&6;}
+ if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`mpfr' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`mpfr' headers and library from TL tree" >&6;}
-  if test "x$with_system_mpfr" != xno; then
-    with_system_mpfr=no
-    ac_configure_args="$ac_configure_args '--without-system-mpfr'"
-  fi
-fi
-if test "x$with_system_mpfr" = xyes; then
-  if test "x$with_system_gmp" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`gmp' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`gmp' headers and library" >&6;}
-    with_system_gmp=yes
-    ac_configure_args="$ac_configure_args '--with-system-gmp'"
-  elif test "x$with_system_gmp" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-mpfr' requires \`--with-system-gmp'" "$LINENO" 5
-  fi
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
 fi
 
-test "x$need_mpfr" = xyes && {
-  need_gmp=yes
-}
-
-## libs/gmp/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/gmp/
-## configure options and TL libraries required for gmp
 
-# Check whether --with-system-gmp was given.
-if test ${with_system_gmp+y}
+AM_DEFAULT_VERBOSITY=1
+# Check whether --enable-silent-rules was given.
+if test ${enable_silent_rules+y}
 then :
-  withval=$with_system_gmp;
+  enableval=$enable_silent_rules;
 fi
 
-# Check whether --with-gmp-includes was given.
-if test ${with_gmp_includes+y}
-then :
-  withval=$with_gmp_includes;
-fi
-
-# Check whether --with-gmp-libdir was given.
-if test ${with_gmp_libdir+y}
+am_make=${MAKE-make}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+printf %s "checking whether $am_make supports nested variables... " >&6; }
+if test ${am_cv_make_support_nested_variables+y}
 then :
-  withval=$with_gmp_libdir;
-fi
-if test "x$with_system_gmp" = x; then
-  if test -f $srcdir/libs/gmp/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`gmp' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`gmp' headers and library from TL tree" >&6;}
-    with_system_gmp=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`gmp' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`gmp' headers and library" >&6;}
-    with_system_gmp=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-gmp=$with_system_gmp'"
-elif test "x$with_system_gmp" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`gmp' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`gmp' headers and library" >&6;}
+  printf %s "(cached) " >&6
+else case e in #(
+  e) if printf "%s\n" 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`gmp' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`gmp' headers and library from TL tree" >&6;}
-  if test "x$with_system_gmp" != xno; then
-    with_system_gmp=no
-    ac_configure_args="$ac_configure_args '--without-system-gmp'"
-  fi
+  am_cv_make_support_nested_variables=no
+fi ;;
+esac
 fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+printf "%s\n" "$am_cv_make_support_nested_variables" >&6; }
+AM_BACKSLASH='\'
 
-## libs/cairo/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/cairo/
-## configure options and TL libraries required for cairo
-
-# Check whether --with-system-cairo was given.
-if test ${with_system_cairo+y}
+am__rm_f_notfound=
+if (rm -f && rm -fr && rm -rf) 2>/dev/null
 then :
-  withval=$with_system_cairo;
-fi
-if test "x$with_system_cairo" = x; then
-  if test -f $srcdir/libs/cairo/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`cairo' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`cairo' headers and library from TL tree" >&6;}
-    with_system_cairo=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`cairo' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`cairo' headers and library" >&6;}
-    with_system_cairo=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-cairo=$with_system_cairo'"
-elif test "x$with_system_cairo" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`cairo' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`cairo' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`cairo' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`cairo' headers and library from TL tree" >&6;}
-  if test "x$with_system_cairo" != xno; then
-    with_system_cairo=no
-    ac_configure_args="$ac_configure_args '--without-system-cairo'"
-  fi
-fi
-if test "x$with_system_cairo" = xyes; then
-  if test "x$with_system_pixman" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`pixman' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`pixman' headers and library" >&6;}
-    with_system_pixman=yes
-    ac_configure_args="$ac_configure_args '--with-system-pixman'"
-  elif test "x$with_system_pixman" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-cairo' requires \`--with-system-pixman'" "$LINENO" 5
-  fi
-fi
 
-test "x$need_cairo" = xyes && {
-  need_pixman=yes
-}
-
-## libs/pixman/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/pixman/
-## configure options and TL libraries required for pixman
-
-# Check whether --with-system-pixman was given.
-if test ${with_system_pixman+y}
-then :
-  withval=$with_system_pixman;
-fi
-if test "x$with_system_pixman" = x; then
-  if test -f $srcdir/libs/pixman/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`pixman' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`pixman' headers and library from TL tree" >&6;}
-    with_system_pixman=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`pixman' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`pixman' headers and library" >&6;}
-    with_system_pixman=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-pixman=$with_system_pixman'"
-elif test "x$with_system_pixman" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`pixman' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`pixman' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`pixman' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`pixman' headers and library from TL tree" >&6;}
-  if test "x$with_system_pixman" != xno; then
-    with_system_pixman=no
-    ac_configure_args="$ac_configure_args '--without-system-pixman'"
-  fi
+else case e in #(
+  e) am__rm_f_notfound='""' ;;
+esac
 fi
 
-## libs/gd/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/gd/
-## configure options and TL libraries required for gd
 
-# Check whether --with-system-gd was given.
-if test ${with_system_gd+y}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking xargs -n works" >&5
+printf %s "checking xargs -n works... " >&6; }
+if test ${am_cv_xargs_n_works+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) if test "`echo 1 2 3 | xargs -n2 echo`" = "1 2
+3"
 then :
-  withval=$with_system_gd;
+  am_cv_xargs_n_works=yes
+else case e in #(
+  e) am_cv_xargs_n_works=no ;;
+esac
+fi ;;
+esac
 fi
-
-# Check whether --with-gd-includes was given.
-if test ${with_gd_includes+y}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_xargs_n_works" >&5
+printf "%s\n" "$am_cv_xargs_n_works" >&6; }
+if test "$am_cv_xargs_n_works" = yes
 then :
-  withval=$with_gd_includes;
+  am__xargs_n='xargs -n'
+else case e in #(
+  e)   am__xargs_n='am__xargs_n () { shift; sed "s/ /\\n/g" | while read am__xargs_n_arg; do "" "$am__xargs_n_arg"; done; }'
+ ;;
+esac
 fi
 
-# Check whether --with-gd-libdir was given.
-if test ${with_gd_libdir+y}
+# Check whether --enable-compiler-warnings was given.
+if test ${enable_compiler_warnings+y}
 then :
-  withval=$with_gd_libdir;
-fi
-if test "x$with_system_gd" = x; then
-  if test -f $srcdir/libs/gd/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`gd' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`gd' headers and library from TL tree" >&6;}
-    with_system_gd=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`gd' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`gd' headers and library" >&6;}
-    with_system_gd=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-gd=$with_system_gd'"
-elif test "x$with_system_gd" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`gd' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`gd' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`gd' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`gd' headers and library from TL tree" >&6;}
-  if test "x$with_system_gd" != xno; then
-    with_system_gd=no
-    ac_configure_args="$ac_configure_args '--without-system-gd'"
-  fi
-fi
-if test "x$with_system_gd" = xyes; then
-  if test "x$with_system_libpng" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`libpng' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`libpng' headers and library" >&6;}
-    with_system_libpng=yes
-    ac_configure_args="$ac_configure_args '--with-system-libpng'"
-  elif test "x$with_system_libpng" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-gd' requires \`--with-system-libpng'" "$LINENO" 5
-  fi
-  if test "x$with_system_freetype2" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`freetype2' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`freetype2' headers and library" >&6;}
-    with_system_freetype2=yes
-    ac_configure_args="$ac_configure_args '--with-system-freetype2'"
-  elif test "x$with_system_freetype2" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-gd' requires \`--with-system-freetype2'" "$LINENO" 5
-  fi
+  enableval=$enable_compiler_warnings;
 fi
+case $enable_compiler_warnings in #(
+  no | min | yes | max | all) :
+     ;; #(
+  *) :
+    if test "x$enable_maintainer_mode" = xyes
+then :
+  enable_compiler_warnings=yes
+else case e in #(
+  e) enable_compiler_warnings=min ;;
+esac
+fi ;;
+esac
 
-test "x$need_gd" = xyes && {
-  need_libpng=yes
-  need_freetype2=yes
-}
+case `pwd` in
+  *\ * | *\	*)
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+printf "%s\n" "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
 
-## libs/potrace/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/potrace/
-## configure options and TL libraries required for potrace
 
-# Check whether --with-system-potrace was given.
-if test ${with_system_potrace+y}
-then :
-  withval=$with_system_potrace;
-fi
 
-# Check whether --with-potrace-includes was given.
-if test ${with_potrace_includes+y}
-then :
-  withval=$with_potrace_includes;
-fi
+macro_version='2.5.4'
+macro_revision='2.5.4'
 
-# Check whether --with-potrace-libdir was given.
-if test ${with_potrace_libdir+y}
-then :
-  withval=$with_potrace_libdir;
-fi
-if test "x$with_system_potrace" = x; then
-  if test -f $srcdir/libs/potrace/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`potrace' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`potrace' headers and library from TL tree" >&6;}
-    with_system_potrace=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`potrace' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`potrace' headers and library" >&6;}
-    with_system_potrace=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-potrace=$with_system_potrace'"
-elif test "x$with_system_potrace" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`potrace' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`potrace' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`potrace' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`potrace' headers and library from TL tree" >&6;}
-  if test "x$with_system_potrace" != xno; then
-    with_system_potrace=no
-    ac_configure_args="$ac_configure_args '--without-system-potrace'"
-  fi
-fi
 
-## libs/freetype2/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/freetype2/
-## configure options and TL libraries required for freetype2
 
-# Check whether --with-system-freetype2 was given.
-if test ${with_system_freetype2+y}
-then :
-  withval=$with_system_freetype2;
-fi
-if test "x$with_system_freetype2" = x; then
-  if test -f $srcdir/libs/freetype2/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`freetype2' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`freetype2' headers and library from TL tree" >&6;}
-    with_system_freetype2=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`freetype2' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`freetype2' headers and library" >&6;}
-    with_system_freetype2=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-freetype2=$with_system_freetype2'"
-elif test "x$with_system_freetype2" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`freetype2' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`freetype2' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`freetype2' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`freetype2' headers and library from TL tree" >&6;}
-  if test "x$with_system_freetype2" != xno; then
-    with_system_freetype2=no
-    ac_configure_args="$ac_configure_args '--without-system-freetype2'"
-  fi
-fi
-if test "x$with_system_freetype2" = xyes; then
-  if test "x$with_system_zlib" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`zlib' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`zlib' headers and library" >&6;}
-    with_system_zlib=yes
-    ac_configure_args="$ac_configure_args '--with-system-zlib'"
-  elif test "x$with_system_zlib" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-freetype2' requires \`--with-system-zlib'" "$LINENO" 5
-  fi
-fi
 
-test "x$need_freetype2" = xyes && {
-  need_zlib=yes
-}
 
-## libs/libpng/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/libpng/
-## configure options and TL libraries required for libpng
 
-# Check whether --with-system-libpng was given.
-if test ${with_system_libpng+y}
-then :
-  withval=$with_system_libpng;
-fi
-if test "x$with_system_libpng" = x; then
-  if test -f $srcdir/libs/libpng/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`libpng' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`libpng' headers and library from TL tree" >&6;}
-    with_system_libpng=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`libpng' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`libpng' headers and library" >&6;}
-    with_system_libpng=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-libpng=$with_system_libpng'"
-elif test "x$with_system_libpng" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`libpng' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`libpng' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`libpng' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`libpng' headers and library from TL tree" >&6;}
-  if test "x$with_system_libpng" != xno; then
-    with_system_libpng=no
-    ac_configure_args="$ac_configure_args '--without-system-libpng'"
-  fi
-fi
-if test "x$with_system_libpng" = xyes; then
-  if test "x$with_system_zlib" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`zlib' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`zlib' headers and library" >&6;}
-    with_system_zlib=yes
-    ac_configure_args="$ac_configure_args '--with-system-zlib'"
-  elif test "x$with_system_zlib" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-libpng' requires \`--with-system-zlib'" "$LINENO" 5
-  fi
-fi
 
-test "x$need_libpng" = xyes && {
-  need_zlib=yes
-}
 
-## libs/libpaper/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/libpaper/
-## configure options and TL libraries required for libpaper
 
-# Check whether --with-system-libpaper was given.
-if test ${with_system_libpaper+y}
-then :
-  withval=$with_system_libpaper;
-fi
 
-# Check whether --with-libpaper-includes was given.
-if test ${with_libpaper_includes+y}
-then :
-  withval=$with_libpaper_includes;
-fi
 
-# Check whether --with-libpaper-libdir was given.
-if test ${with_libpaper_libdir+y}
-then :
-  withval=$with_libpaper_libdir;
-fi
-if test "x$with_system_libpaper" = x; then
-  if test -f $srcdir/libs/libpaper/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`libpaper' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`libpaper' headers and library from TL tree" >&6;}
-    with_system_libpaper=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`libpaper' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`libpaper' headers and library" >&6;}
-    with_system_libpaper=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-libpaper=$with_system_libpaper'"
-elif test "x$with_system_libpaper" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`libpaper' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`libpaper' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`libpaper' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`libpaper' headers and library from TL tree" >&6;}
-  if test "x$with_system_libpaper" != xno; then
-    with_system_libpaper=no
-    ac_configure_args="$ac_configure_args '--without-system-libpaper'"
-  fi
-fi
 
-## libs/luajit/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/luajit/
-## configure options and TL libraries required for luajit
 
-# Check whether --with-system-luajit was given.
-if test ${with_system_luajit+y}
-then :
-  withval=$with_system_luajit;
-fi
-if test "x$with_system_luajit" = x; then
-  if test -f $srcdir/libs/luajit/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`luajit' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`luajit' headers and library from TL tree" >&6;}
-    with_system_luajit=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`luajit' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`luajit' headers and library" >&6;}
-    with_system_luajit=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-luajit=$with_system_luajit'"
-elif test "x$with_system_luajit" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`luajit' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`luajit' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`luajit' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`luajit' headers and library from TL tree" >&6;}
-  if test "x$with_system_luajit" != xno; then
-    with_system_luajit=no
-    ac_configure_args="$ac_configure_args '--without-system-luajit'"
-  fi
-fi
 
-## libs/lua53/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/lua53/
-## configure options and TL libraries required for lua53
+ltmain=$ac_aux_dir/ltmain.sh
 
-## libs/zlib/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory libs/zlib/
-## configure options and TL libraries required for zlib
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
 
-# Check whether --with-system-zlib was given.
-if test ${with_system_zlib+y}
-then :
-  withval=$with_system_zlib;
-fi
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
 
-# Check whether --with-zlib-includes was given.
-if test ${with_zlib_includes+y}
-then :
-  withval=$with_zlib_includes;
-fi
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
 
-# Check whether --with-zlib-libdir was given.
-if test ${with_zlib_libdir+y}
-then :
-  withval=$with_zlib_libdir;
-fi
-if test "x$with_system_zlib" = x; then
-  if test -f $srcdir/libs/zlib/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`zlib' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`zlib' headers and library from TL tree" >&6;}
-    with_system_zlib=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`zlib' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`zlib' headers and library" >&6;}
-    with_system_zlib=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-zlib=$with_system_zlib'"
-elif test "x$with_system_zlib" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`zlib' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`zlib' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`zlib' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`zlib' headers and library from TL tree" >&6;}
-  if test "x$with_system_zlib" != xno; then
-    with_system_zlib=no
-    ac_configure_args="$ac_configure_args '--without-system-zlib'"
-  fi
-fi
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
 
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
 
-## texk/ptexenc/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/ptexenc/
-## configure options and TL libraries required for ptexenc
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
 
-# Check whether --with-system-ptexenc was given.
-if test ${with_system_ptexenc+y}
-then :
-  withval=$with_system_ptexenc;
-fi
-if test "x$with_system_ptexenc" = x; then
-  if test -f $srcdir/texk/ptexenc/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`ptexenc' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`ptexenc' headers and library from TL tree" >&6;}
-    with_system_ptexenc=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`ptexenc' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`ptexenc' headers and library" >&6;}
-    with_system_ptexenc=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-ptexenc=$with_system_ptexenc'"
-elif test "x$with_system_ptexenc" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`ptexenc' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`ptexenc' headers and library" >&6;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+printf %s "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`ptexenc' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`ptexenc' headers and library from TL tree" >&6;}
-  if test "x$with_system_ptexenc" != xno; then
-    with_system_ptexenc=no
-    ac_configure_args="$ac_configure_args '--without-system-ptexenc'"
-  fi
-fi
-if test "x$with_system_ptexenc" = xyes; then
-  if test "x$with_system_kpathsea" = x; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ->  installed \`kpathsea' headers and library" >&5
-printf "%s\n" "$as_me:   ->  installed \`kpathsea' headers and library" >&6;}
-    with_system_kpathsea=yes
-    ac_configure_args="$ac_configure_args '--with-system-kpathsea'"
-  elif test "x$with_system_kpathsea" != xyes; then
-    as_fn_error $? "Sorry, \`--with-system-ptexenc' requires \`--with-system-kpathsea'" "$LINENO" 5
-  fi
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
 fi
 
-test "x$need_ptexenc" = xyes && {
-  need_kpathsea=yes
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO ""
 }
 
-## texk/kpathsea/ac/withenable.ac: configure.ac fragment for the TeX Live subdirectory texk/kpathsea/
-## configure options and TL libraries required for kpathsea
+case $ECHO in
+  printf*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+printf "%s\n" "printf" >&6; } ;;
+  print*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+printf "%s\n" "print -r" >&6; } ;;
+  *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+printf "%s\n" "cat" >&6; } ;;
+esac
 
-# Check whether --with-system-kpathsea was given.
-if test ${with_system_kpathsea+y}
-then :
-  withval=$with_system_kpathsea;
-fi
-if test "x$with_system_kpathsea" = x; then
-  if test -f $srcdir/texk/kpathsea/configure; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming \`kpathsea' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Assuming \`kpathsea' headers and library from TL tree" >&6;}
-    with_system_kpathsea=no
-  else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Assuming installed \`kpathsea' headers and library" >&5
-printf "%s\n" "$as_me: Assuming installed \`kpathsea' headers and library" >&6;}
-    with_system_kpathsea=yes
-  fi
-  ac_configure_args="$ac_configure_args '--with-system-kpathsea=$with_system_kpathsea'"
-elif test "x$with_system_kpathsea" = xyes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using installed \`kpathsea' headers and library" >&5
-printf "%s\n" "$as_me: Using installed \`kpathsea' headers and library" >&6;}
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using \`kpathsea' headers and library from TL tree" >&5
-printf "%s\n" "$as_me: Using \`kpathsea' headers and library from TL tree" >&6;}
-  if test "x$with_system_kpathsea" != xno; then
-    with_system_kpathsea=no
-    ac_configure_args="$ac_configure_args '--without-system-kpathsea'"
-  fi
-fi
 
-## texk/kpathsea/ac/mktex.ac: configure.ac fragment for the TeX Live
-## subdirectory texk/kpathsea.
-## configure defaults for mktexfmt & Co.
-# Check whether --enable-mktexmf-default was given.
-if test ${enable_mktexmf_default+y}
-then :
-  enableval=$enable_mktexmf_default;
-fi
-# Check whether --enable-mktexpk-default was given.
-if test ${enable_mktexpk_default+y}
-then :
-  enableval=$enable_mktexpk_default;
-fi
-# Check whether --enable-mktextfm-default was given.
-if test ${enable_mktextfm_default+y}
-then :
-  enableval=$enable_mktextfm_default;
-fi
-# Check whether --enable-mkocp-default was given.
-if test ${enable_mkocp_default+y}
-then :
-  enableval=$enable_mkocp_default;
-fi
-# Check whether --enable-mkofm-default was given.
-if test ${enable_mkofm_default+y}
-then :
-  enableval=$enable_mkofm_default;
-fi
-# Check whether --enable-mktexfmt-default was given.
-if test ${enable_mktexfmt_default+y}
-then :
-  enableval=$enable_mktexfmt_default;
-fi
-# Check whether --enable-mktextex-default was given.
-if test ${enable_mktextex_default+y}
-then :
-  enableval=$enable_mktextex_default;
-fi
 
 
 
-# end of kpse_setup macro.
-echo 'tldbg:KPSE_SETUP done (toplevel=)' >&5
 
 
-am__api_version='1.17'
 
 
-  # Find a good install program.  We prefer a C program (faster),
-# so one script is as good as another.  But avoid the broken or
-# incompatible versions:
-# SysV /etc/install, /usr/sbin/install
-# SunOS /usr/etc/install
-# IRIX /sbin/install
-# AIX /bin/install
-# AmigaOS /C/install, which installs bootblocks on floppy discs
-# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
-# AFS /usr/afsws/bin/install, which mishandles nonexistent args
-# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
-# OS/2's system install, which has a completely different semantic
-# ./install, which can be erroneously created by make from ./install.sh.
-# Reject install programs that cannot install multiple files.
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
-printf %s "checking for a BSD-compatible install... " >&6; }
-if test -z "$INSTALL"; then
-if test ${ac_cv_path_install+y}
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+printf %s "checking for a sed that does not truncate output... " >&6; }
+if test ${ac_cv_path_SED+y}
 then :
   printf %s "(cached) " >&6
 else case e in #(
-  e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+  e)           ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+     for ac_i in 1 2 3 4 5 6 7; do
+       ac_script="$ac_script$as_nl$ac_script"
+     done
+     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+     { ac_script=; unset ac_script;}
+     if test -z "$SED"; then
+  ac_path_SED_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 for as_dir in $PATH
 do
   IFS=$as_save_IFS
@@ -7947,892 +6817,51 @@ do
     */) ;;
     *) as_dir=$as_dir/ ;;
   esac
-    # Account for fact that we put trailing slashes in our PATH walk.
-case $as_dir in #((
-  ./ | /[cC]/* | \
-  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
-  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
-  /usr/ucb/* ) ;;
-  *)
-    # OSF1 and SCO ODT 3.0 have their own names for install.
-    # Don't use installbsd from OSF since it installs stuff as root
-    # by default.
-    for ac_prog in ginstall scoinst install; do
-      for ac_exec_ext in '' $ac_executable_extensions; do
-	if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then
-	  if test $ac_prog = install &&
-	    grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
-	    # AIX install.  It has an incompatible calling convention.
-	    :
-	  elif test $ac_prog = install &&
-	    grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
-	    # program-specific install script used by HP pwplus--don't use.
-	    :
-	  else
-	    rm -rf conftest.one conftest.two conftest.dir
-	    echo one > conftest.one
-	    echo two > conftest.two
-	    mkdir conftest.dir
-	    if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" &&
-	      test -s conftest.one && test -s conftest.two &&
-	      test -s conftest.dir/conftest.one &&
-	      test -s conftest.dir/conftest.two
-	    then
-	      ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c"
-	      break 3
-	    fi
-	  fi
-	fi
-      done
-    done
-    ;;
+    for ac_prog in sed gsed
+   do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_SED="$as_dir$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+  # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in #(
+*GNU*)
+  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+#(
+*)
+  ac_count=0
+  printf %s 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    printf "%s\n" '' >> "conftest.nl"
+    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_SED_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_SED="$ac_path_SED"
+      ac_path_SED_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
 esac
 
+      $ac_path_SED_found && break 3
+    done
+  done
   done
 IFS=$as_save_IFS
-
-rm -rf conftest.one conftest.two conftest.dir
- ;;
-esac
-fi
-  if test ${ac_cv_path_install+y}; then
-    INSTALL=$ac_cv_path_install
-  else
-    # As a last resort, use the slow shell script.  Don't cache a
-    # value for INSTALL within a source directory, because that will
-    # break other packages using the cache if that directory is
-    # removed, or if the value is a relative name.
-    INSTALL=$ac_install_sh
-  fi
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
-printf "%s\n" "$INSTALL" >&6; }
-
-# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
-# It thinks the first close brace ends the variable substitution.
-test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
-
-test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
-
-test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether sleep supports fractional seconds" >&5
-printf %s "checking whether sleep supports fractional seconds... " >&6; }
-if test ${am_cv_sleep_fractional_seconds+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if sleep 0.001 2>/dev/null
-then :
-  am_cv_sleep_fractional_seconds=yes
-else case e in #(
-  e) am_cv_sleep_fractional_seconds=no ;;
-esac
-fi
- ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_sleep_fractional_seconds" >&5
-printf "%s\n" "$am_cv_sleep_fractional_seconds" >&6; }
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking filesystem timestamp resolution" >&5
-printf %s "checking filesystem timestamp resolution... " >&6; }
-if test ${am_cv_filesystem_timestamp_resolution+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) # Default to the worst case.
-am_cv_filesystem_timestamp_resolution=2
-
-# Only try to go finer than 1 sec if sleep can do it.
-# Don't try 1 sec, because if 0.01 sec and 0.1 sec don't work,
-# - 1 sec is not much of a win compared to 2 sec, and
-# - it takes 2 seconds to perform the test whether 1 sec works.
-#
-# Instead, just use the default 2s on platforms that have 1s resolution,
-# accept the extra 1s delay when using $sleep in the Automake tests, in
-# exchange for not incurring the 2s delay for running the test for all
-# packages.
-#
-am_try_resolutions=
-if test "$am_cv_sleep_fractional_seconds" = yes; then
-  # Even a millisecond often causes a bunch of false positives,
-  # so just try a hundredth of a second. The time saved between .001 and
-  # .01 is not terribly consequential.
-  am_try_resolutions="0.01 0.1 $am_try_resolutions"
-fi
-
-# In order to catch current-generation FAT out, we must *modify* files
-# that already exist; the *creation* timestamp is finer.  Use names
-# that make ls -t sort them differently when they have equal
-# timestamps than when they have distinct timestamps, keeping
-# in mind that ls -t prints the *newest* file first.
-rm -f conftest.ts?
-: > conftest.ts1
-: > conftest.ts2
-: > conftest.ts3
-
-# Make sure ls -t actually works.  Do 'set' in a subshell so we don't
-# clobber the current shell's arguments. (Outer-level square brackets
-# are removed by m4; they're present so that m4 does not expand
-# <dollar><star>; be careful, easy to get confused.)
-if (
-     set X `ls -t conftest.ts[12]` &&
-     {
-       test "$*" != "X conftest.ts1 conftest.ts2" ||
-       test "$*" != "X conftest.ts2 conftest.ts1";
-     }
-); then :; else
-  # If neither matched, then we have a broken ls.  This can happen
-  # if, for instance, CONFIG_SHELL is bash and it inherits a
-  # broken ls alias from the environment.  This has actually
-  # happened.  Such a system could not be considered "sane".
-  printf "%s\n" ""Bad output from ls -t: \"`ls -t conftest.ts[12]`\""" >&5
-  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
-as_fn_error $? "ls -t produces unexpected output.
-Make sure there is not a broken ls alias in your environment.
-See 'config.log' for more details" "$LINENO" 5; }
-fi
-
-for am_try_res in $am_try_resolutions; do
-  # Any one fine-grained sleep might happen to cross the boundary
-  # between two values of a coarser actual resolution, but if we do
-  # two fine-grained sleeps in a row, at least one of them will fall
-  # entirely within a coarse interval.
-  echo alpha > conftest.ts1
-  sleep $am_try_res
-  echo beta > conftest.ts2
-  sleep $am_try_res
-  echo gamma > conftest.ts3
-
-  # We assume that 'ls -t' will make use of high-resolution
-  # timestamps if the operating system supports them at all.
-  if (set X `ls -t conftest.ts?` &&
-      test "$2" = conftest.ts3 &&
-      test "$3" = conftest.ts2 &&
-      test "$4" = conftest.ts1); then
-    #
-    # Ok, ls -t worked. If we're at a resolution of 1 second, we're done,
-    # because we don't need to test make.
-    make_ok=true
-    if test $am_try_res != 1; then
-      # But if we've succeeded so far with a subsecond resolution, we
-      # have one more thing to check: make. It can happen that
-      # everything else supports the subsecond mtimes, but make doesn't;
-      # notably on macOS, which ships make 3.81 from 2006 (the last one
-      # released under GPLv2). https://bugs.gnu.org/68808
-      #
-      # We test $MAKE if it is defined in the environment, else "make".
-      # It might get overridden later, but our hope is that in practice
-      # it does not matter: it is the system "make" which is (by far)
-      # the most likely to be broken, whereas if the user overrides it,
-      # probably they did so with a better, or at least not worse, make.
-      # https://lists.gnu.org/archive/html/automake/2024-06/msg00051.html
-      #
-      # Create a Makefile (real tab character here):
-      rm -f conftest.mk
-      echo 'conftest.ts1: conftest.ts2' >conftest.mk
-      echo '	touch conftest.ts2' >>conftest.mk
-      #
-      # Now, running
-      #   touch conftest.ts1; touch conftest.ts2; make
-      # should touch ts1 because ts2 is newer. This could happen by luck,
-      # but most often, it will fail if make's support is insufficient. So
-      # test for several consecutive successes.
-      #
-      # (We reuse conftest.ts[12] because we still want to modify existing
-      # files, not create new ones, per above.)
-      n=0
-      make=${MAKE-make}
-      until test $n -eq 3; do
-        echo one > conftest.ts1
-        sleep $am_try_res
-        echo two > conftest.ts2 # ts2 should now be newer than ts1
-        if $make -f conftest.mk | grep 'up to date' >/dev/null; then
-          make_ok=false
-          break # out of $n loop
-        fi
-        n=`expr $n + 1`
-      done
-    fi
-    #
-    if $make_ok; then
-      # Everything we know to check worked out, so call this resolution good.
-      am_cv_filesystem_timestamp_resolution=$am_try_res
-      break # out of $am_try_res loop
-    fi
-    # Otherwise, we'll go on to check the next resolution.
-  fi
-done
-rm -f conftest.ts?
-# (end _am_filesystem_timestamp_resolution)
- ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_filesystem_timestamp_resolution" >&5
-printf "%s\n" "$am_cv_filesystem_timestamp_resolution" >&6; }
-
-# This check should not be cached, as it may vary across builds of
-# different projects.
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
-printf %s "checking whether build environment is sane... " >&6; }
-# Reject unsafe characters in $srcdir or the absolute working directory
-# name.  Accept space and tab only in the latter.
-am_lf='
-'
-case `pwd` in
-  *[\\\"\#\$\&\'\`$am_lf]*)
-    as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
-esac
-case $srcdir in
-  *[\\\"\#\$\&\'\`$am_lf\ \	]*)
-    as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
-esac
-
-# Do 'set' in a subshell so we don't clobber the current shell's
-# arguments.  Must try -L first in case configure is actually a
-# symlink; some systems play weird games with the mod time of symlinks
-# (eg FreeBSD returns the mod time of the symlink's containing
-# directory).
-am_build_env_is_sane=no
-am_has_slept=no
-rm -f conftest.file
-for am_try in 1 2; do
-  echo "timestamp, slept: $am_has_slept" > conftest.file
-  if (
-    set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
-    if test "$*" = "X"; then
-      # -L didn't work.
-      set X `ls -t "$srcdir/configure" conftest.file`
-    fi
-    test "$2" = conftest.file
-  ); then
-    am_build_env_is_sane=yes
-    break
-  fi
-  # Just in case.
-  sleep "$am_cv_filesystem_timestamp_resolution"
-  am_has_slept=yes
-done
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_build_env_is_sane" >&5
-printf "%s\n" "$am_build_env_is_sane" >&6; }
-if test "$am_build_env_is_sane" = no; then
-  as_fn_error $? "newly created file is older than distributed files!
-Check your system clock" "$LINENO" 5
-fi
-
-# If we didn't sleep, we still need to ensure time stamps of config.status and
-# generated files are strictly newer.
-am_sleep_pid=
-if test -e conftest.file || grep 'slept: no' conftest.file >/dev/null 2>&1
-then :
-
-else case e in #(
-  e)   ( sleep "$am_cv_filesystem_timestamp_resolution" ) &
-  am_sleep_pid=$!
- ;;
-esac
-fi
-
-rm -f conftest.file
-
-test "$program_prefix" != NONE &&
-  program_transform_name="s&^&$program_prefix&;$program_transform_name"
-# Use a double $ so make ignores it.
-test "$program_suffix" != NONE &&
-  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
-# Double any \ or $.
-# By default was 's,x,x', remove it if useless.
-ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
-program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"`
-
-
-  if test x"${MISSING+set}" != xset; then
-  MISSING="\${SHELL} '$am_aux_dir/missing'"
-fi
-# Use eval to expand $SHELL
-if eval "$MISSING --is-lightweight"; then
-  am_missing_run="$MISSING "
-else
-  am_missing_run=
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
-printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
-fi
-
-if test x"${install_sh+set}" != xset; then
-  case $am_aux_dir in
-  *\ * | *\	*)
-    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
-  *)
-    install_sh="\${SHELL} $am_aux_dir/install-sh"
-  esac
-fi
-
-# Installed binaries are usually stripped using 'strip' when the user
-# run "make install-strip".  However 'strip' might not be the right
-# tool to use in cross-compilation environments, therefore Automake
-# will honor the 'STRIP' environment variable to overrule this program.
-if test "$cross_compiling" != no; then
-  if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
-set dummy ${ac_tool_prefix}strip; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_STRIP+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test -n "$STRIP"; then
-  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi ;;
-esac
-fi
-STRIP=$ac_cv_prog_STRIP
-if test -n "$STRIP"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
-printf "%s\n" "$STRIP" >&6; }
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-fi
-
-
-fi
-if test -z "$ac_cv_prog_STRIP"; then
-  ac_ct_STRIP=$STRIP
-  # Extract the first word of "strip", so it can be a program name with args.
-set dummy strip; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_ac_ct_STRIP+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test -n "$ac_ct_STRIP"; then
-  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_STRIP="strip"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi ;;
-esac
-fi
-ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
-if test -n "$ac_ct_STRIP"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
-printf "%s\n" "$ac_ct_STRIP" >&6; }
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-fi
-
-  if test "x$ac_ct_STRIP" = x; then
-    STRIP=":"
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
-    STRIP=$ac_ct_STRIP
-  fi
-else
-  STRIP="$ac_cv_prog_STRIP"
-fi
-
-fi
-INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
-
-
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5
-printf %s "checking for a race-free mkdir -p... " >&6; }
-if test -z "$MKDIR_P"; then
-  if test ${ac_cv_path_mkdir+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_prog in mkdir gmkdir; do
-	 for ac_exec_ext in '' $ac_executable_extensions; do
-	   as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue
-	   case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #(
-	     'mkdir ('*'coreutils) '* | \
-	     *'BusyBox '* | \
-	     'mkdir (fileutils) '4.1*)
-	       ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext
-	       break 3;;
-	   esac
-	 done
-       done
-  done
-IFS=$as_save_IFS
- ;;
-esac
-fi
-
-  test -d ./--version && rmdir ./--version
-  if test ${ac_cv_path_mkdir+y}; then
-    MKDIR_P="$ac_cv_path_mkdir -p"
-  else
-    # As a last resort, use plain mkdir -p,
-    # in the hope it doesn't have the bugs of ancient mkdir.
-    MKDIR_P='mkdir -p'
-  fi
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
-printf "%s\n" "$MKDIR_P" >&6; }
-
-for ac_prog in gawk mawk nawk awk
-do
-  # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_AWK+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test -n "$AWK"; then
-  ac_cv_prog_AWK="$AWK" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_AWK="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi ;;
-esac
-fi
-AWK=$ac_cv_prog_AWK
-if test -n "$AWK"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
-printf "%s\n" "$AWK" >&6; }
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-fi
-
-
-  test -n "$AWK" && break
-done
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
-printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
-set x ${MAKE-make}
-ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
-if eval test \${ac_cv_prog_make_${ac_make}_set+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) cat >conftest.make <<\_ACEOF
-SHELL = /bin/sh
-all:
-	@echo '@@@%%%=$(MAKE)=@@@%%%'
-_ACEOF
-# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
-case `${MAKE-make} -f conftest.make 2>/dev/null` in
-  *@@@%%%=?*=@@@%%%*)
-    eval ac_cv_prog_make_${ac_make}_set=yes;;
-  *)
-    eval ac_cv_prog_make_${ac_make}_set=no;;
-esac
-rm -f conftest.make ;;
-esac
-fi
-if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
-  SET_MAKE=
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-  SET_MAKE="MAKE=${MAKE-make}"
-fi
-
-rm -rf .tst 2>/dev/null
-mkdir .tst 2>/dev/null
-if test -d .tst; then
-  am__leading_dot=.
-else
-  am__leading_dot=_
-fi
-rmdir .tst 2>/dev/null
-
-DEPDIR="${am__leading_dot}deps"
-
-ac_config_commands="$ac_config_commands depfiles"
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
-printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; }
-cat > confinc.mk << 'END'
-am__doit:
-	@echo this is the am__doit target >confinc.out
-.PHONY: am__doit
-END
-am__include="#"
-am__quote=
-# BSD make does it like this.
-echo '.include "confinc.mk" # ignored' > confmf.BSD
-# Other make implementations (GNU, Solaris 10, AIX) do it like this.
-echo 'include confinc.mk # ignored' > confmf.GNU
-_am_result=no
-for s in GNU BSD; do
-  { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
-   (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
-   ac_status=$?
-   echo "$as_me:$LINENO: \$? = $ac_status" >&5
-   (exit $ac_status); }
-  case $?:`cat confinc.out 2>/dev/null` in #(
-  '0:this is the am__doit target') :
-    case $s in #(
-  BSD) :
-    am__include='.include' am__quote='"' ;; #(
-  *) :
-    am__include='include' am__quote='' ;;
-esac ;; #(
-  *) :
-     ;;
-esac
-  if test "$am__include" != "#"; then
-    _am_result="yes ($s style)"
-    break
-  fi
-done
-rm -f confinc.* confmf.*
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
-printf "%s\n" "${_am_result}" >&6; }
-
-# Check whether --enable-dependency-tracking was given.
-if test ${enable_dependency_tracking+y}
-then :
-  enableval=$enable_dependency_tracking;
-fi
-
-if test "x$enable_dependency_tracking" != xno; then
-  am_depcomp="$ac_aux_dir/depcomp"
-  AMDEPBACKSLASH='\'
-  am__nodep='_no'
-fi
- if test "x$enable_dependency_tracking" != xno; then
-  AMDEP_TRUE=
-  AMDEP_FALSE='#'
-else
-  AMDEP_TRUE='#'
-  AMDEP_FALSE=
-fi
-
-
-AM_DEFAULT_VERBOSITY=1
-# Check whether --enable-silent-rules was given.
-if test ${enable_silent_rules+y}
-then :
-  enableval=$enable_silent_rules;
-fi
-
-am_make=${MAKE-make}
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
-printf %s "checking whether $am_make supports nested variables... " >&6; }
-if test ${am_cv_make_support_nested_variables+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if printf "%s\n" 'TRUE=$(BAR$(V))
-BAR0=false
-BAR1=true
-V=1
-am__doit:
-	@$(TRUE)
-.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
-  am_cv_make_support_nested_variables=yes
-else
-  am_cv_make_support_nested_variables=no
-fi ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
-printf "%s\n" "$am_cv_make_support_nested_variables" >&6; }
-AM_BACKSLASH='\'
-
-am__rm_f_notfound=
-if (rm -f && rm -fr && rm -rf) 2>/dev/null
-then :
-
-else case e in #(
-  e) am__rm_f_notfound='""' ;;
-esac
-fi
-
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking xargs -n works" >&5
-printf %s "checking xargs -n works... " >&6; }
-if test ${am_cv_xargs_n_works+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test "`echo 1 2 3 | xargs -n2 echo`" = "1 2
-3"
-then :
-  am_cv_xargs_n_works=yes
-else case e in #(
-  e) am_cv_xargs_n_works=no ;;
-esac
-fi ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_xargs_n_works" >&5
-printf "%s\n" "$am_cv_xargs_n_works" >&6; }
-if test "$am_cv_xargs_n_works" = yes
-then :
-  am__xargs_n='xargs -n'
-else case e in #(
-  e)   am__xargs_n='am__xargs_n () { shift; sed "s/ /\\n/g" | while read am__xargs_n_arg; do "" "$am__xargs_n_arg"; done; }'
- ;;
-esac
-fi
-
-# Check whether --enable-compiler-warnings was given.
-if test ${enable_compiler_warnings+y}
-then :
-  enableval=$enable_compiler_warnings;
-fi
-case $enable_compiler_warnings in #(
-  no | min | yes | max | all) :
-     ;; #(
-  *) :
-    if test "x$enable_maintainer_mode" = xyes
-then :
-  enable_compiler_warnings=yes
-else case e in #(
-  e) enable_compiler_warnings=min ;;
-esac
-fi ;;
-esac
-
-case `pwd` in
-  *\ * | *\	*)
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
-printf "%s\n" "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
-esac
-
-
-
-macro_version='2.5.4'
-macro_revision='2.5.4'
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-ltmain=$ac_aux_dir/ltmain.sh
-
-# Backslashify metacharacters that are still active within
-# double-quoted strings.
-sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
-
-# Same as above, but do not quote variable references.
-double_quote_subst='s/\(["`\\]\)/\\\1/g'
-
-# Sed substitution to delay expansion of an escaped shell variable in a
-# double_quote_subst'ed string.
-delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
-
-# Sed substitution to delay expansion of an escaped single quote.
-delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
-
-# Sed substitution to avoid accidental globbing in evaled expressions
-no_glob_subst='s/\*/\\\*/g'
-
-ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
-ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
-ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
-printf %s "checking how to print strings... " >&6; }
-# Test print first, because it will be a builtin if present.
-if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
-   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
-  ECHO='print -r --'
-elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
-  ECHO='printf %s\n'
-else
-  # Use this function as a fallback that always works.
-  func_fallback_echo ()
-  {
-    eval 'cat <<_LTECHO_EOF
-$1
-_LTECHO_EOF'
-  }
-  ECHO='func_fallback_echo'
-fi
-
-# func_echo_all arg...
-# Invoke $ECHO with all args, space-separated.
-func_echo_all ()
-{
-    $ECHO ""
-}
-
-case $ECHO in
-  printf*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: printf" >&5
-printf "%s\n" "printf" >&6; } ;;
-  print*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
-printf "%s\n" "print -r" >&6; } ;;
-  *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cat" >&5
-printf "%s\n" "cat" >&6; } ;;
-esac
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
-printf %s "checking for a sed that does not truncate output... " >&6; }
-if test ${ac_cv_path_SED+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e)           ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
-     for ac_i in 1 2 3 4 5 6 7; do
-       ac_script="$ac_script$as_nl$ac_script"
-     done
-     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
-     { ac_script=; unset ac_script;}
-     if test -z "$SED"; then
-  ac_path_SED_found=false
-  # Loop through the user's path and test for each of PROGNAME-LIST
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_prog in sed gsed
-   do
-    for ac_exec_ext in '' $ac_executable_extensions; do
-      ac_path_SED="$as_dir$ac_prog$ac_exec_ext"
-      as_fn_executable_p "$ac_path_SED" || continue
-# Check for GNU ac_path_SED and select it if it is found.
-  # Check for GNU $ac_path_SED
-case `"$ac_path_SED" --version 2>&1` in #(
-*GNU*)
-  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
-#(
-*)
-  ac_count=0
-  printf %s 0123456789 >"conftest.in"
-  while :
-  do
-    cat "conftest.in" "conftest.in" >"conftest.tmp"
-    mv "conftest.tmp" "conftest.in"
-    cp "conftest.in" "conftest.nl"
-    printf "%s\n" '' >> "conftest.nl"
-    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
-    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
-    as_fn_arith $ac_count + 1 && ac_count=$as_val
-    if test $ac_count -gt ${ac_path_SED_max-0}; then
-      # Best one so far, save it but keep looking for a better one
-      ac_cv_path_SED="$ac_path_SED"
-      ac_path_SED_max=$ac_count
-    fi
-    # 10*(2^10) chars as input seems more than enough
-    test $ac_count -gt 10 && break
-  done
-  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
-esac
-
-      $ac_path_SED_found && break 3
-    done
-  done
-  done
-IFS=$as_save_IFS
-  if test -z "$ac_cv_path_SED"; then
-    as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
-  fi
-else
-  ac_cv_path_SED=$SED
-fi
+  if test -z "$ac_cv_path_SED"; then
+    as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+  fi
+else
+  ac_cv_path_SED=$SED
+fi
  ;;
 esac
 fi
@@ -12549,7 +10578,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='tex-live'
- VERSION='2024-03-10'
+ VERSION='2025-03-07'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -24669,397 +22698,129 @@ if test -n "$hardcode_libdir_flag_spec_CXX" ||
     hardcode_action_CXX=immediate
   fi
 else
-  # We cannot hardcode anything, or else we can only hardcode existing
-  # directories.
-  hardcode_action_CXX=unsupported
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5
-printf "%s\n" "$hardcode_action_CXX" >&6; }
-
-if test relink = "$hardcode_action_CXX" ||
-   test yes = "$inherit_rpath_CXX"; then
-  # Fast installation is not supported
-  enable_fast_install=no
-elif test yes = "$shlibpath_overrides_runpath" ||
-     test no = "$enable_shared"; then
-  # Fast installation is not necessary
-  enable_fast_install=needless
-fi
-
-
-
-
-
-
-
-  fi # test -n "$compiler"
-
-  CC=$lt_save_CC
-  CFLAGS=$lt_save_CFLAGS
-  LDCXX=$LD
-  LD=$lt_save_LD
-  GCC=$lt_save_GCC
-  with_gnu_ld=$lt_save_with_gnu_ld
-  lt_cv_path_LDCXX=$lt_cv_path_LD
-  lt_cv_path_LD=$lt_save_path_LD
-  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
-  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
-fi # test yes != "$_lt_caught_CXX_error"
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
-printf %s "checking whether ln -s works... " >&6; }
-LN_S=$as_ln_s
-if test "$LN_S" = "ln -s"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
-printf "%s\n" "no, using $LN_S" >&6; }
-fi
-
-
-# Check whether --with-ln-s was given.
-if test ${with_ln_s+y}
-then :
-  withval=$with_ln_s;
-fi
-if test "x$LN_S" != "xln -s" && test "x$kpse_cv_have_win32" = xno && test "x$with_ln_s" != xno; then
-  as_fn_error $? "You could use \`--without-ln-s' to build without working \`ln -s'" "$LINENO" 5
-fi
-
-if test "x$enable_native_texlive_build" = xyes; then
-  if test "x$datarootdir" = 'x${prefix}/share'; then
-    datarootdir='${prefix}'
-    ac_configure_args="$ac_configure_args '--datarootdir=$datarootdir'"
-  fi
-  if test "x$mandir" = 'x${datarootdir}/man'; then
-    mandir='${prefix}/texmf-dist/doc/man'
-    ac_configure_args="$ac_configure_args '--mandir=$mandir'"
-  fi
-  if test "x$infodir" = 'x${datarootdir}/info'; then
-    infodir='${prefix}/texmf-dist/doc/info'
-    ac_configure_args="$ac_configure_args '--infodir=$infodir'"
-  fi
-fi
-
-if test "x$enable_web2c" = xyes && test "x$with_system_kpathsea" = xyes; then
-  if test "x$with_kpathsea_includes" = x; then
-    list="/usr/include /usr/local/include"
-  else
-    list=$with_kpathsea_includes
-  fi
-  found=no
-  for ac_dir in $list; do
-    if test -r "$ac_dir/kpathsea/paths.h"; then
-      found=yes
-      break
-    fi
-  done
-  if test "x$found" = xno; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: You requested to build \`web2c' using an installed \`kpathsea' version," >&5
-printf "%s\n" "$as_me: You requested to build \`web2c' using an installed \`kpathsea' version," >&6;}
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:     which requires to locate the <kpathsea/paths.h> header file." >&5
-printf "%s\n" "$as_me:     which requires to locate the <kpathsea/paths.h> header file." >&6;}
-    as_fn_error $? "Sorry, not found under any of: $list *****" "$LINENO" 5
-  fi
-fi
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for TeX specific libraries to build" >&5
-printf %s "checking for TeX specific libraries to build... " >&6; }
-echo 'tldbg:_KPSE_RECURSE called: list=texlibs, text=TeX specific libraries, cond=test "x$with_system_[]Kpse_pkg" != xyes && test "x$need_[]Kpse_pkg" = xyes, prefix=texk/.' >&5
-MAKE_SUBDIRS=
-CONF_SUBDIRS=
-if test -x $srcdir/texk/ptexenc/configure; then
-  test "x$with_system_ptexenc" != xyes && test "x$need_ptexenc" = xyes && MAKE_SUBDIRS="texk/ptexenc $MAKE_SUBDIRS"
-  CONF_SUBDIRS="texk/ptexenc $CONF_SUBDIRS"
-else
-  echo 'tldbg:_KPSE_RECURSE skipping subdir, no (executable) configure: '"$srcdir"'/texk/ptexenc/configure' >&5
-fi
-if test -x $srcdir/texk/kpathsea/configure; then
-  test "x$with_system_kpathsea" != xyes && test "x$need_kpathsea" = xyes && MAKE_SUBDIRS="texk/kpathsea $MAKE_SUBDIRS"
-  CONF_SUBDIRS="texk/kpathsea $CONF_SUBDIRS"
-else
-  echo 'tldbg:_KPSE_RECURSE skipping subdir, no (executable) configure: '"$srcdir"'/texk/kpathsea/configure' >&5
-fi
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAKE_SUBDIRS" >&5
-printf "%s\n" "$MAKE_SUBDIRS" >&6; }
-
-kpse_save_CPPFLAGS=$CPPFLAGS
-kpse_save_LIBS=$LIBS
-
-syslib_status=yes
-syslib_used=no
-if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
-set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_PKG_CONFIG+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test -n "$PKG_CONFIG"; then
-  ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_PKG_CONFIG="${ac_tool_prefix}pkg-config"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi ;;
-esac
-fi
-PKG_CONFIG=$ac_cv_prog_PKG_CONFIG
-if test -n "$PKG_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
-printf "%s\n" "$PKG_CONFIG" >&6; }
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-fi
-
-
-fi
-if test -z "$ac_cv_prog_PKG_CONFIG"; then
-  ac_ct_PKG_CONFIG=$PKG_CONFIG
-  # Extract the first word of "pkg-config", so it can be a program name with args.
-set dummy pkg-config; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_ac_ct_PKG_CONFIG+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test -n "$ac_ct_PKG_CONFIG"; then
-  ac_cv_prog_ac_ct_PKG_CONFIG="$ac_ct_PKG_CONFIG" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_PKG_CONFIG="pkg-config"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi ;;
-esac
-fi
-ac_ct_PKG_CONFIG=$ac_cv_prog_ac_ct_PKG_CONFIG
-if test -n "$ac_ct_PKG_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PKG_CONFIG" >&5
-printf "%s\n" "$ac_ct_PKG_CONFIG" >&6; }
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-fi
-
-  if test "x$ac_ct_PKG_CONFIG" = x; then
-    PKG_CONFIG="false"
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
-    PKG_CONFIG=$ac_ct_PKG_CONFIG
-  fi
-else
-  PKG_CONFIG="$ac_cv_prog_PKG_CONFIG"
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action_CXX=unsupported
 fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5
+printf "%s\n" "$hardcode_action_CXX" >&6; }
 
-if $PKG_CONFIG kpathsea; then
-  KPATHSEA_INCLUDES=`$PKG_CONFIG kpathsea --cflags`
-  KPATHSEA_LIBS=`$PKG_CONFIG kpathsea --libs`
-elif test "x$need_kpathsea:$with_system_kpathsea" = xyes:yes; then
-  as_fn_error $? "did not find kpathsea" "$LINENO" 5
+if test relink = "$hardcode_action_CXX" ||
+   test yes = "$inherit_rpath_CXX"; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+     test no = "$enable_shared"; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
 fi
 
-if $PKG_CONFIG ptexenc; then
-  PTEXENC_INCLUDES=`$PKG_CONFIG ptexenc --cflags`
-  PTEXENC_LIBS=`$PKG_CONFIG ptexenc --libs`
-elif test "x$need_ptexenc:$with_system_ptexenc" = xyes:yes; then
-  as_fn_error $? "did not find ptexenc" "$LINENO" 5
-fi
 
-##tldbg _KPSE_LIB_FLAGS_SYSTEM: zlib (z).
-if test "x$with_zlib_includes" != x && test "x$with_zlib_includes" != xyes; then
-  ZLIB_INCLUDES="-I$with_zlib_includes"
-fi
-ZLIB_LIBS="-lz"
-if test "x$with_zlib_libdir" != x && test "x$with_zlib_libdir" != xyes; then
-  ZLIB_LIBS="-L$with_zlib_libdir $ZLIB_LIBS"
-fi
 
-if $PKG_CONFIG PPLIB; then
-  PPLIB_INCLUDES=`$PKG_CONFIG PPLIB --cflags`
-  PPLIB_LIBS=`$PKG_CONFIG PPLIB --libs`
-elif test "x$need_PPLIB:$with_system_PPLIB" = xyes:yes; then
-  as_fn_error $? "did not find PPLIB" "$LINENO" 5
-fi
 
-##tldbg _KPSE_LIB_FLAGS_SYSTEM: potrace (potrace).
-if test "x$with_potrace_includes" != x && test "x$with_potrace_includes" != xyes; then
-  POTRACE_INCLUDES="-I$with_potrace_includes"
-fi
-POTRACE_LIBS="-lpotrace"
-if test "x$with_potrace_libdir" != x && test "x$with_potrace_libdir" != xyes; then
-  POTRACE_LIBS="-L$with_potrace_libdir $POTRACE_LIBS"
-fi
 
-##tldbg _KPSE_LIB_FLAGS_SYSTEM: libpaper (paper).
-if test "x$with_libpaper_includes" != x && test "x$with_libpaper_includes" != xyes; then
-  LIBPAPER_INCLUDES="-I$with_libpaper_includes"
-fi
-LIBPAPER_LIBS="-lpaper"
-if test "x$with_libpaper_libdir" != x && test "x$with_libpaper_libdir" != xyes; then
-  LIBPAPER_LIBS="-L$with_libpaper_libdir $LIBPAPER_LIBS"
-fi
 
-if $PKG_CONFIG libpng; then
-  LIBPNG_INCLUDES=`$PKG_CONFIG libpng --cflags`
-  LIBPNG_LIBS=`$PKG_CONFIG libpng --libs`
-elif test "x$need_libpng:$with_system_libpng" = xyes:yes; then
-  as_fn_error $? "did not find libpng" "$LINENO" 5
-fi
 
-if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}freetype-config", so it can be a program name with args.
-set dummy ${ac_tool_prefix}freetype-config; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_FT2_CONFIG+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test -n "$FT2_CONFIG"; then
-  ac_cv_prog_FT2_CONFIG="$FT2_CONFIG" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_FT2_CONFIG="${ac_tool_prefix}freetype-config"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
+  fi # test -n "$compiler"
 
-fi ;;
-esac
-fi
-FT2_CONFIG=$ac_cv_prog_FT2_CONFIG
-if test -n "$FT2_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FT2_CONFIG" >&5
-printf "%s\n" "$FT2_CONFIG" >&6; }
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+  LDCXX=$LD
+  LD=$lt_save_LD
+  GCC=$lt_save_GCC
+  with_gnu_ld=$lt_save_with_gnu_ld
+  lt_cv_path_LDCXX=$lt_cv_path_LD
+  lt_cv_path_LD=$lt_save_path_LD
+  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test yes != "$_lt_caught_CXX_error"
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+printf %s "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+printf "%s\n" "no, using $LN_S" >&6; }
 fi
 
 
-fi
-if test -z "$ac_cv_prog_FT2_CONFIG"; then
-  ac_ct_FT2_CONFIG=$FT2_CONFIG
-  # Extract the first word of "freetype-config", so it can be a program name with args.
-set dummy freetype-config; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_ac_ct_FT2_CONFIG+y}
+# Check whether --with-ln-s was given.
+if test ${with_ln_s+y}
 then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) if test -n "$ac_ct_FT2_CONFIG"; then
-  ac_cv_prog_ac_ct_FT2_CONFIG="$ac_ct_FT2_CONFIG" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  case $as_dir in #(((
-    '') as_dir=./ ;;
-    */) ;;
-    *) as_dir=$as_dir/ ;;
-  esac
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_FT2_CONFIG="freetype-config"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi ;;
-esac
+  withval=$with_ln_s;
 fi
-ac_ct_FT2_CONFIG=$ac_cv_prog_ac_ct_FT2_CONFIG
-if test -n "$ac_ct_FT2_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FT2_CONFIG" >&5
-printf "%s\n" "$ac_ct_FT2_CONFIG" >&6; }
-else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+if test "x$LN_S" != "xln -s" && test "x$kpse_cv_have_win32" = xno && test "x$with_ln_s" != xno; then
+  as_fn_error $? "You could use \`--without-ln-s' to build without working \`ln -s'" "$LINENO" 5
 fi
 
-  if test "x$ac_ct_FT2_CONFIG" = x; then
-    FT2_CONFIG="false"
+if test "x$enable_native_texlive_build" = xyes; then
+  if test "x$datarootdir" = 'x${prefix}/share'; then
+    datarootdir='${prefix}'
+    ac_configure_args="$ac_configure_args '--datarootdir=$datarootdir'"
+  fi
+  if test "x$mandir" = 'x${datarootdir}/man'; then
+    mandir='${prefix}/texmf-dist/doc/man'
+    ac_configure_args="$ac_configure_args '--mandir=$mandir'"
+  fi
+  if test "x$infodir" = 'x${datarootdir}/info'; then
+    infodir='${prefix}/texmf-dist/doc/info'
+    ac_configure_args="$ac_configure_args '--infodir=$infodir'"
+  fi
+fi
+
+if test "x$enable_web2c" = xyes && test "x$with_system_kpathsea" = xyes; then
+  if test "x$with_kpathsea_includes" = x; then
+    list="/usr/include /usr/local/include"
   else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
-    FT2_CONFIG=$ac_ct_FT2_CONFIG
+    list=$with_kpathsea_includes
+  fi
+  found=no
+  for ac_dir in $list; do
+    if test -r "$ac_dir/kpathsea/paths.h"; then
+      found=yes
+      break
+    fi
+  done
+  if test "x$found" = xno; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: You requested to build \`web2c' using an installed \`kpathsea' version," >&5
+printf "%s\n" "$as_me: You requested to build \`web2c' using an installed \`kpathsea' version," >&6;}
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}:     which requires to locate the <kpathsea/paths.h> header file." >&5
+printf "%s\n" "$as_me:     which requires to locate the <kpathsea/paths.h> header file." >&6;}
+    as_fn_error $? "Sorry, not found under any of: $list *****" "$LINENO" 5
   fi
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for TeX specific libraries to build" >&5
+printf %s "checking for TeX specific libraries to build... " >&6; }
+echo 'tldbg:_KPSE_RECURSE called: list=texlibs, text=TeX specific libraries, cond=test "x$with_system_[]Kpse_pkg" != xyes && test "x$need_[]Kpse_pkg" = xyes, prefix=texk/.' >&5
+MAKE_SUBDIRS=
+CONF_SUBDIRS=
+if test -x $srcdir/texk/kpathsea/configure; then
+  test "x$with_system_kpathsea" != xyes && test "x$need_kpathsea" = xyes && MAKE_SUBDIRS="texk/kpathsea $MAKE_SUBDIRS"
+  CONF_SUBDIRS="texk/kpathsea $CONF_SUBDIRS"
 else
-  FT2_CONFIG="$ac_cv_prog_FT2_CONFIG"
+  echo 'tldbg:_KPSE_RECURSE skipping subdir, no (executable) configure: '"$srcdir"'/texk/kpathsea/configure' >&5
 fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAKE_SUBDIRS" >&5
+printf "%s\n" "$MAKE_SUBDIRS" >&6; }
+
+kpse_save_CPPFLAGS=$CPPFLAGS
+kpse_save_LIBS=$LIBS
+
+syslib_status=yes
+syslib_used=no
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
 set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
@@ -25163,101 +22924,73 @@ esac
 else
   PKG_CONFIG="$ac_cv_prog_PKG_CONFIG"
 fi
-if $FT2_CONFIG --ftversion >/dev/null 2>&1; then
-  FREETYPE2_INCLUDES=`$FT2_CONFIG --cflags`
-  FREETYPE2_LIBS=`$FT2_CONFIG --libs`
-elif $PKG_CONFIG --libs freetype2 >/dev/null 2>&1; then
-  FREETYPE2_INCLUDES=`$PKG_CONFIG --cflags freetype2`
-  FREETYPE2_LIBS=`$PKG_CONFIG --libs freetype2`
-elif test "x$need_freetype2:$with_system_freetype2" = xyes:yes; then
-  as_fn_error $? "did not find freetype-config required for system freetype2 library" "$LINENO" 5
-fi
-
-##tldbg _KPSE_LIB_FLAGS_SYSTEM: gd (gd).
-if test "x$with_gd_includes" != x && test "x$with_gd_includes" != xyes; then
-  GD_INCLUDES="-I$with_gd_includes"
-fi
-GD_LIBS="-lgd"
-if test "x$with_gd_libdir" != x && test "x$with_gd_libdir" != xyes; then
-  GD_LIBS="-L$with_gd_libdir $GD_LIBS"
-fi
 
-if $PKG_CONFIG pixman-1 --atleast-version=0.18; then
-  PIXMAN_INCLUDES=`$PKG_CONFIG pixman-1 --cflags`
-  PIXMAN_LIBS=`$PKG_CONFIG pixman-1 --libs`
-elif test "x$need_pixman:$with_system_pixman" = xyes:yes; then
-  as_fn_error $? "did not find pixman-1 0.18 or better" "$LINENO" 5
+if $PKG_CONFIG kpathsea; then
+  KPATHSEA_INCLUDES=`$PKG_CONFIG kpathsea --cflags`
+  KPATHSEA_LIBS=`$PKG_CONFIG kpathsea --libs`
+elif test "x$need_kpathsea:$with_system_kpathsea" = xyes:yes; then
+  as_fn_error $? "did not find kpathsea" "$LINENO" 5
 fi
 
-if $PKG_CONFIG cairo --atleast-version=1.12; then
-  CAIRO_INCLUDES=`$PKG_CONFIG cairo --cflags`
-  CAIRO_LIBS=`$PKG_CONFIG cairo --libs`
-elif test "x$need_cairo:$with_system_cairo" = xyes:yes; then
-  as_fn_error $? "did not find cairo 1.12 or better" "$LINENO" 5
+if $PKG_CONFIG ptexenc; then
+  PTEXENC_INCLUDES=`$PKG_CONFIG ptexenc --cflags`
+  PTEXENC_LIBS=`$PKG_CONFIG ptexenc --libs`
+elif test "x$need_ptexenc:$with_system_ptexenc" = xyes:yes; then
+  as_fn_error $? "did not find ptexenc" "$LINENO" 5
 fi
 
-##tldbg _KPSE_LIB_FLAGS_SYSTEM: gmp (gmp).
-if test "x$with_gmp_includes" != x && test "x$with_gmp_includes" != xyes; then
-  GMP_INCLUDES="-I$with_gmp_includes"
+##tldbg _KPSE_LIB_FLAGS_SYSTEM: zlib (z).
+if test "x$with_zlib_includes" != x && test "x$with_zlib_includes" != xyes; then
+  ZLIB_INCLUDES="-I$with_zlib_includes"
 fi
-GMP_LIBS="-lgmp"
-if test "x$with_gmp_libdir" != x && test "x$with_gmp_libdir" != xyes; then
-  GMP_LIBS="-L$with_gmp_libdir $GMP_LIBS"
+ZLIB_LIBS="-lz"
+if test "x$with_zlib_libdir" != x && test "x$with_zlib_libdir" != xyes; then
+  ZLIB_LIBS="-L$with_zlib_libdir $ZLIB_LIBS"
 fi
 
-##tldbg _KPSE_LIB_FLAGS_SYSTEM: mpfr (mpfr).
-if test "x$with_mpfr_includes" != x && test "x$with_mpfr_includes" != xyes; then
-  MPFR_INCLUDES="-I$with_mpfr_includes"
-fi
-MPFR_LIBS="-lmpfr"
-if test "x$with_mpfr_libdir" != x && test "x$with_mpfr_libdir" != xyes; then
-  MPFR_LIBS="-L$with_mpfr_libdir $MPFR_LIBS"
+if $PKG_CONFIG PPLIB; then
+  PPLIB_INCLUDES=`$PKG_CONFIG PPLIB --cflags`
+  PPLIB_LIBS=`$PKG_CONFIG PPLIB --libs`
+elif test "x$need_PPLIB:$with_system_PPLIB" = xyes:yes; then
+  as_fn_error $? "did not find PPLIB" "$LINENO" 5
 fi
 
-##tldbg _KPSE_LIB_FLAGS_SYSTEM: mpfi (mpfi).
-if test "x$with_mpfi_includes" != x && test "x$with_mpfi_includes" != xyes; then
-  MPFI_INCLUDES="-I$with_mpfi_includes"
+##tldbg _KPSE_LIB_FLAGS_SYSTEM: potrace (potrace).
+if test "x$with_potrace_includes" != x && test "x$with_potrace_includes" != xyes; then
+  POTRACE_INCLUDES="-I$with_potrace_includes"
 fi
-MPFI_LIBS="-lmpfi"
-if test "x$with_mpfi_libdir" != x && test "x$with_mpfi_libdir" != xyes; then
-  MPFI_LIBS="-L$with_mpfi_libdir $MPFI_LIBS"
+POTRACE_LIBS="-lpotrace"
+if test "x$with_potrace_libdir" != x && test "x$with_potrace_libdir" != xyes; then
+  POTRACE_LIBS="-L$with_potrace_libdir $POTRACE_LIBS"
 fi
 
-: "kpse_xpdf_system_flags - no-op"
-
-if $PKG_CONFIG zziplib --atleast-version=0.12; then
-  ZZIPLIB_INCLUDES=`$PKG_CONFIG zziplib --cflags`
-  ZZIPLIB_LIBS=`$PKG_CONFIG zziplib --libs`
-elif test "x$need_zziplib:$with_system_zziplib" = xyes:yes; then
-  as_fn_error $? "did not find zziplib 0.12 or better" "$LINENO" 5
+##tldbg _KPSE_LIB_FLAGS_SYSTEM: libpaper (paper).
+if test "x$with_libpaper_includes" != x && test "x$with_libpaper_includes" != xyes; then
+  LIBPAPER_INCLUDES="-I$with_libpaper_includes"
 fi
-
-if $PKG_CONFIG graphite2; then
-  GRAPHITE2_INCLUDES=`$PKG_CONFIG graphite2 --cflags`
-  GRAPHITE2_LIBS=`$PKG_CONFIG graphite2 --libs`
-elif test "x$need_graphite2:$with_system_graphite2" = xyes:yes; then
-  as_fn_error $? "did not find graphite2" "$LINENO" 5
+LIBPAPER_LIBS="-lpaper"
+if test "x$with_libpaper_libdir" != x && test "x$with_libpaper_libdir" != xyes; then
+  LIBPAPER_LIBS="-L$with_libpaper_libdir $LIBPAPER_LIBS"
 fi
 
-if $PKG_CONFIG teckit; then
-  TECKIT_INCLUDES=`$PKG_CONFIG teckit --cflags`
-  TECKIT_LIBS=`$PKG_CONFIG teckit --libs`
-elif test "x$need_teckit:$with_system_teckit" = xyes:yes; then
-  as_fn_error $? "did not find teckit" "$LINENO" 5
+if $PKG_CONFIG libpng; then
+  LIBPNG_INCLUDES=`$PKG_CONFIG libpng --cflags`
+  LIBPNG_LIBS=`$PKG_CONFIG libpng --libs`
+elif test "x$need_libpng:$with_system_libpng" = xyes:yes; then
+  as_fn_error $? "did not find libpng" "$LINENO" 5
 fi
 
-echo 'tldbg:KPSE_ICU_SYSTEM_FLAGS called.' >&5
 if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}icu-config", so it can be a program name with args.
-set dummy ${ac_tool_prefix}icu-config; ac_word=$2
+  # Extract the first word of "${ac_tool_prefix}freetype-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}freetype-config; ac_word=$2
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_ICU_CONFIG+y}
+if test ${ac_cv_prog_FT2_CONFIG+y}
 then :
   printf %s "(cached) " >&6
 else case e in #(
-  e) if test -n "$ICU_CONFIG"; then
-  ac_cv_prog_ICU_CONFIG="$ICU_CONFIG" # Let the user override the test.
+  e) if test -n "$FT2_CONFIG"; then
+  ac_cv_prog_FT2_CONFIG="$FT2_CONFIG" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 for as_dir in $PATH
@@ -25270,7 +23003,7 @@ do
   esac
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ICU_CONFIG="${ac_tool_prefix}icu-config"
+    ac_cv_prog_FT2_CONFIG="${ac_tool_prefix}freetype-config"
     printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
@@ -25281,10 +23014,10 @@ IFS=$as_save_IFS
 fi ;;
 esac
 fi
-ICU_CONFIG=$ac_cv_prog_ICU_CONFIG
-if test -n "$ICU_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ICU_CONFIG" >&5
-printf "%s\n" "$ICU_CONFIG" >&6; }
+FT2_CONFIG=$ac_cv_prog_FT2_CONFIG
+if test -n "$FT2_CONFIG"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FT2_CONFIG" >&5
+printf "%s\n" "$FT2_CONFIG" >&6; }
 else
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
 printf "%s\n" "no" >&6; }
@@ -25292,18 +23025,18 @@ fi
 
 
 fi
-if test -z "$ac_cv_prog_ICU_CONFIG"; then
-  ac_ct_ICU_CONFIG=$ICU_CONFIG
-  # Extract the first word of "icu-config", so it can be a program name with args.
-set dummy icu-config; ac_word=$2
+if test -z "$ac_cv_prog_FT2_CONFIG"; then
+  ac_ct_FT2_CONFIG=$FT2_CONFIG
+  # Extract the first word of "freetype-config", so it can be a program name with args.
+set dummy freetype-config; ac_word=$2
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
-if test ${ac_cv_prog_ac_ct_ICU_CONFIG+y}
+if test ${ac_cv_prog_ac_ct_FT2_CONFIG+y}
 then :
   printf %s "(cached) " >&6
 else case e in #(
-  e) if test -n "$ac_ct_ICU_CONFIG"; then
-  ac_cv_prog_ac_ct_ICU_CONFIG="$ac_ct_ICU_CONFIG" # Let the user override the test.
+  e) if test -n "$ac_ct_FT2_CONFIG"; then
+  ac_cv_prog_ac_ct_FT2_CONFIG="$ac_ct_FT2_CONFIG" # Let the user override the test.
 else
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 for as_dir in $PATH
@@ -25316,7 +23049,7 @@ do
   esac
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_ICU_CONFIG="icu-config"
+    ac_cv_prog_ac_ct_FT2_CONFIG="freetype-config"
     printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
@@ -25327,17 +23060,17 @@ IFS=$as_save_IFS
 fi ;;
 esac
 fi
-ac_ct_ICU_CONFIG=$ac_cv_prog_ac_ct_ICU_CONFIG
-if test -n "$ac_ct_ICU_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_ICU_CONFIG" >&5
-printf "%s\n" "$ac_ct_ICU_CONFIG" >&6; }
+ac_ct_FT2_CONFIG=$ac_cv_prog_ac_ct_FT2_CONFIG
+if test -n "$ac_ct_FT2_CONFIG"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FT2_CONFIG" >&5
+printf "%s\n" "$ac_ct_FT2_CONFIG" >&6; }
 else
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
 printf "%s\n" "no" >&6; }
 fi
 
-  if test "x$ac_ct_ICU_CONFIG" = x; then
-    ICU_CONFIG="false"
+  if test "x$ac_ct_FT2_CONFIG" = x; then
+    FT2_CONFIG="false"
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
@@ -25345,10 +23078,10 @@ yes:)
 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
-    ICU_CONFIG=$ac_ct_ICU_CONFIG
+    FT2_CONFIG=$ac_ct_FT2_CONFIG
   fi
 else
-  ICU_CONFIG="$ac_cv_prog_ICU_CONFIG"
+  FT2_CONFIG="$ac_cv_prog_FT2_CONFIG"
 fi
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
@@ -25453,368 +23186,341 @@ esac
 else
   PKG_CONFIG="$ac_cv_prog_PKG_CONFIG"
 fi
-if $ICU_CONFIG --version >/dev/null 2>&1; then
-  ICU_INCLUDES=`$ICU_CONFIG --cppflags`
-  ICU_LIBS=`$ICU_CONFIG --ldflags-searchpath --ldflags-libsonly --ldflags-system`
-elif $PKG_CONFIG --libs icu-uc icu-io >/dev/null 2>&1; then
-  ICU_INCLUDES=`$PKG_CONFIG --cflags icu-uc icu-io`
-  ICU_LIBS=`$PKG_CONFIG --libs icu-uc icu-io`
-elif test "x$need_icu:$with_system_icu" = xyes:yes; then
-  as_fn_error $? "did not find either pkg-config or icu-config; one is required for system icu library support" "$LINENO" 5
+if $FT2_CONFIG --ftversion >/dev/null 2>&1; then
+  FREETYPE2_INCLUDES=`$FT2_CONFIG --cflags`
+  FREETYPE2_LIBS=`$FT2_CONFIG --libs`
+elif $PKG_CONFIG --libs freetype2 >/dev/null 2>&1; then
+  FREETYPE2_INCLUDES=`$PKG_CONFIG --cflags freetype2`
+  FREETYPE2_LIBS=`$PKG_CONFIG --libs freetype2`
+elif test "x$need_freetype2:$with_system_freetype2" = xyes:yes; then
+  as_fn_error $? "did not find freetype-config required for system freetype2 library" "$LINENO" 5
 fi
 
-if $PKG_CONFIG harfbuzz; then
-  HARFBUZZ_INCLUDES=`$PKG_CONFIG harfbuzz --cflags`
-  HARFBUZZ_LIBS=`$PKG_CONFIG harfbuzz --libs`
-elif test "x$need_harfbuzz:$with_system_harfbuzz" = xyes:yes; then
-  as_fn_error $? "did not find harfbuzz" "$LINENO" 5
+##tldbg _KPSE_LIB_FLAGS_SYSTEM: gd (gd).
+if test "x$with_gd_includes" != x && test "x$with_gd_includes" != xyes; then
+  GD_INCLUDES="-I$with_gd_includes"
 fi
-
-if $PKG_CONFIG luajit; then
-  LUAJIT_INCLUDES=`$PKG_CONFIG luajit --cflags`
-  LUAJIT_LIBS=`$PKG_CONFIG luajit --libs`
-elif test "x$need_luajit:$with_system_luajit" = xyes:yes; then
-  as_fn_error $? "did not find luajit" "$LINENO" 5
+GD_LIBS="-lgd"
+if test "x$with_gd_libdir" != x && test "x$with_gd_libdir" != xyes; then
+  GD_LIBS="-L$with_gd_libdir $GD_LIBS"
 fi
 
+if $PKG_CONFIG pixman-1 --atleast-version=0.18; then
+  PIXMAN_INCLUDES=`$PKG_CONFIG pixman-1 --cflags`
+  PIXMAN_LIBS=`$PKG_CONFIG pixman-1 --libs`
+elif test "x$need_pixman:$with_system_pixman" = xyes:yes; then
+  as_fn_error $? "did not find pixman-1 0.18 or better" "$LINENO" 5
+fi
 
+if $PKG_CONFIG cairo --atleast-version=1.12; then
+  CAIRO_INCLUDES=`$PKG_CONFIG cairo --cflags`
+  CAIRO_LIBS=`$PKG_CONFIG cairo --libs`
+elif test "x$need_cairo:$with_system_cairo" = xyes:yes; then
+  as_fn_error $? "did not find cairo 1.12 or better" "$LINENO" 5
+fi
 
+##tldbg _KPSE_LIB_FLAGS_SYSTEM: gmp (gmp).
+if test "x$with_gmp_includes" != x && test "x$with_gmp_includes" != xyes; then
+  GMP_INCLUDES="-I$with_gmp_includes"
+fi
+GMP_LIBS="-lgmp"
+if test "x$with_gmp_libdir" != x && test "x$with_gmp_libdir" != xyes; then
+  GMP_LIBS="-L$with_gmp_libdir $GMP_LIBS"
+fi
 
-
-
-## texk/kpathsea/ac/kpathsea.ac: configure.ac fragment for the TeX Live subdirectory texk/kpathsea/
-## basic check of system kpathsea
-if test "x$need_kpathsea:$with_system_kpathsea" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`kpathsea' library" >&5
-printf %s "checking requested system \`kpathsea' library... " >&6; }
-  CPPFLAGS="$KPATHSEA_INCLUDES $CPPFLAGS"
-  LIBS="$KPATHSEA_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <kpathsea/kpathsea.h>
-#include <kpathsea/version.h>
-int
-main (void)
-{
-const char *version = kpathsea_version_string;
-kpse_set_program_name("prog", "name");
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
-esac
+##tldbg _KPSE_LIB_FLAGS_SYSTEM: mpfr (mpfr).
+if test "x$with_mpfr_includes" != x && test "x$with_mpfr_includes" != xyes; then
+  MPFR_INCLUDES="-I$with_mpfr_includes"
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+MPFR_LIBS="-lmpfr"
+if test "x$with_mpfr_libdir" != x && test "x$with_mpfr_libdir" != xyes; then
+  MPFR_LIBS="-L$with_mpfr_libdir $MPFR_LIBS"
 fi
 
-## texk/ptexenc/ac/ptexenc.ac: configure.ac fragment for the TeX Live subdirectory texk/ptexenc/
-## basic check of system ptexenc
-if test "x$need_ptexenc:$with_system_ptexenc" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`ptexenc' library" >&5
-printf %s "checking requested system \`ptexenc' library... " >&6; }
-  CPPFLAGS="$PTEXENC_INCLUDES $CPPFLAGS"
-  LIBS="$PTEXENC_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <ptexenc/ptexenc.h>
-int
-main (void)
-{
-const char *version = ptexenc_version_string;
-set_enc_string("prog", "name");
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
-esac
+##tldbg _KPSE_LIB_FLAGS_SYSTEM: mpfi (mpfi).
+if test "x$with_mpfi_includes" != x && test "x$with_mpfi_includes" != xyes; then
+  MPFI_INCLUDES="-I$with_mpfi_includes"
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+MPFI_LIBS="-lmpfi"
+if test "x$with_mpfi_libdir" != x && test "x$with_mpfi_libdir" != xyes; then
+  MPFI_LIBS="-L$with_mpfi_libdir $MPFI_LIBS"
+fi
+
+: "kpse_xpdf_system_flags - no-op"
+
+if $PKG_CONFIG zziplib --atleast-version=0.12; then
+  ZZIPLIB_INCLUDES=`$PKG_CONFIG zziplib --cflags`
+  ZZIPLIB_LIBS=`$PKG_CONFIG zziplib --libs`
+elif test "x$need_zziplib:$with_system_zziplib" = xyes:yes; then
+  as_fn_error $? "did not find zziplib 0.12 or better" "$LINENO" 5
 fi
 
-## libs/zlib/ac/zlib.ac: configure.ac fragment for the TeX Live subdirectory libs/zlib/
-## basic check of system zlib
-if test "x$need_zlib:$with_system_zlib" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`zlib' library" >&5
-printf %s "checking requested system \`zlib' library... " >&6; }
-  CPPFLAGS="$ZLIB_INCLUDES $CPPFLAGS"
-  LIBS="$ZLIB_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <zlib.h>
-int
-main (void)
-{
-z_stream stream;
-const char *version = zlibVersion();
-deflate(&stream, 0);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
-esac
+if $PKG_CONFIG graphite2; then
+  GRAPHITE2_INCLUDES=`$PKG_CONFIG graphite2 --cflags`
+  GRAPHITE2_LIBS=`$PKG_CONFIG graphite2 --libs`
+elif test "x$need_graphite2:$with_system_graphite2" = xyes:yes; then
+  as_fn_error $? "did not find graphite2" "$LINENO" 5
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+
+if $PKG_CONFIG teckit; then
+  TECKIT_INCLUDES=`$PKG_CONFIG teckit --cflags`
+  TECKIT_LIBS=`$PKG_CONFIG teckit --libs`
+elif test "x$need_teckit:$with_system_teckit" = xyes:yes; then
+  as_fn_error $? "did not find teckit" "$LINENO" 5
 fi
 
-## libs/luajit/ac/luajit.ac: configure.ac fragment for the TeX Live subdirectory libs/luajit/
-## basic check of system luajit
-if test "x$need_luajit:$with_system_luajit" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`luajit' library" >&5
-printf %s "checking requested system \`luajit' library... " >&6; }
-  CPPFLAGS="$LUAJIT_INCLUDES $CPPFLAGS"
-  LIBS="$LUAJIT_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <luajit.h>
-int
-main (void)
-{
-const char *v = LUAJIT_VERSION;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
+echo 'tldbg:KPSE_ICU_SYSTEM_FLAGS called.' >&5
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}icu-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}icu-config; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ICU_CONFIG+y}
 then :
-  syslib_used=yes kpse_res=ok
+  printf %s "(cached) " >&6
 else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
+  e) if test -n "$ICU_CONFIG"; then
+  ac_cv_prog_ICU_CONFIG="$ICU_CONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ICU_CONFIG="${ac_tool_prefix}icu-config"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi ;;
 esac
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+ICU_CONFIG=$ac_cv_prog_ICU_CONFIG
+if test -n "$ICU_CONFIG"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ICU_CONFIG" >&5
+printf "%s\n" "$ICU_CONFIG" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
 fi
 
-## libs/libpaper/ac/libpaper.ac: configure.ac fragment for the TeX Live subdirectory libs/libpaper/
-## basic check of system libpaper
-if test "x$need_libpaper:$with_system_libpaper" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`libpaper' library" >&5
-printf %s "checking requested system \`libpaper' library... " >&6; }
-  CPPFLAGS="$LIBPAPER_INCLUDES $CPPFLAGS"
-  LIBS="$LIBPAPER_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <paper.h>
-int
-main (void)
-{
-const char *v = defaultpapername();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
+
+fi
+if test -z "$ac_cv_prog_ICU_CONFIG"; then
+  ac_ct_ICU_CONFIG=$ICU_CONFIG
+  # Extract the first word of "icu-config", so it can be a program name with args.
+set dummy icu-config; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_ICU_CONFIG+y}
 then :
-  syslib_used=yes kpse_res=ok
+  printf %s "(cached) " >&6
 else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
+  e) if test -n "$ac_ct_ICU_CONFIG"; then
+  ac_cv_prog_ac_ct_ICU_CONFIG="$ac_ct_ICU_CONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_ICU_CONFIG="icu-config"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi ;;
 esac
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+ac_ct_ICU_CONFIG=$ac_cv_prog_ac_ct_ICU_CONFIG
+if test -n "$ac_ct_ICU_CONFIG"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_ICU_CONFIG" >&5
+printf "%s\n" "$ac_ct_ICU_CONFIG" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
 fi
 
-## libs/libpng/ac/libpng.ac: configure.ac fragment for the TeX Live subdirectory libs/libpng/
-## basic check of system libpng
-if test "x$need_libpng:$with_system_libpng" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`libpng' library" >&5
-printf %s "checking requested system \`libpng' library... " >&6; }
-  CPPFLAGS="$LIBPNG_INCLUDES $CPPFLAGS"
-  LIBS="$LIBPNG_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <png.h>
-int
-main (void)
-{
-png_structp png; png_voidp io; png_rw_ptr fn;
-png_set_read_fn(png, io, fn);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
+  if test "x$ac_ct_ICU_CONFIG" = x; then
+    ICU_CONFIG="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
 esac
+    ICU_CONFIG=$ac_ct_ICU_CONFIG
+  fi
+else
+  ICU_CONFIG="$ac_cv_prog_ICU_CONFIG"
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
-fi
-
-## libs/freetype2/ac/freetype2.ac: configure.ac fragment for the TeX Live subdirectory libs/freetype2/
-## basic check of system freetype2
-if test "x$need_freetype2:$with_system_freetype2" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`freetype2' library" >&5
-printf %s "checking requested system \`freetype2' library... " >&6; }
-  CPPFLAGS="$FREETYPE2_INCLUDES $CPPFLAGS"
-  LIBS="$FREETYPE2_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <ft2build.h>
-#include FT_FREETYPE_H
-int
-main (void)
-{
-FT_Library library; FT_Init_FreeType(&library);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_PKG_CONFIG+y}
 then :
-  syslib_used=yes kpse_res=ok
+  printf %s "(cached) " >&6
 else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
-esac
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
-fi
+  e) if test -n "$PKG_CONFIG"; then
+  ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_PKG_CONFIG="${ac_tool_prefix}pkg-config"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
-## libs/potrace/ac/potrace.ac: configure.ac fragment for the TeX Live subdirectory libs/potrace/
-## basic check of system potrace
-if test "x$need_potrace:$with_system_potrace" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`potrace' library" >&5
-printf %s "checking requested system \`potrace' library... " >&6; }
-  CPPFLAGS="$POTRACE_INCLUDES $CPPFLAGS"
-  LIBS="$POTRACE_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <potracelib.h>
-int
-main (void)
-{
-potrace_state_t st;
-const char *version = potrace_version();
-potrace_state_free(&st);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
+fi ;;
 esac
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+PKG_CONFIG=$ac_cv_prog_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+printf "%s\n" "$PKG_CONFIG" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
 fi
 
-## libs/gd/ac/gd.ac: configure.ac fragment for the TeX Live subdirectory libs/gd/
-## basic check of system gd
-if test "x$need_gd:$with_system_gd" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`gd' library" >&5
-printf %s "checking requested system \`gd' library... " >&6; }
-  CPPFLAGS="$GD_INCLUDES $CPPFLAGS"
-  LIBS="$GD_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <gd.h>
-int
-main (void)
-{
-gdImageCreate(1, 2);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
+
+fi
+if test -z "$ac_cv_prog_PKG_CONFIG"; then
+  ac_ct_PKG_CONFIG=$PKG_CONFIG
+  # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_PKG_CONFIG+y}
 then :
-  syslib_used=yes kpse_res=ok
+  printf %s "(cached) " >&6
 else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
+  e) if test -n "$ac_ct_PKG_CONFIG"; then
+  ac_cv_prog_ac_ct_PKG_CONFIG="$ac_ct_PKG_CONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_PKG_CONFIG="pkg-config"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi ;;
 esac
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+ac_ct_PKG_CONFIG=$ac_cv_prog_ac_ct_PKG_CONFIG
+if test -n "$ac_ct_PKG_CONFIG"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PKG_CONFIG" >&5
+printf "%s\n" "$ac_ct_PKG_CONFIG" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
 fi
 
-## libs/pixman/ac/pixman.ac: configure.ac fragment for the TeX Live subdirectory libs/pixman/
-## basic check of system pixman
-if test "x$need_pixman:$with_system_pixman" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`pixman' library" >&5
-printf %s "checking requested system \`pixman' library... " >&6; }
-  CPPFLAGS="$PIXMAN_INCLUDES $CPPFLAGS"
-  LIBS="$PIXMAN_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <pixman.h>
-int
-main (void)
-{
-const char *s = pixman_version_string();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
+  if test "x$ac_ct_PKG_CONFIG" = x; then
+    PKG_CONFIG="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
 esac
+    PKG_CONFIG=$ac_ct_PKG_CONFIG
+  fi
+else
+  PKG_CONFIG="$ac_cv_prog_PKG_CONFIG"
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
+if $ICU_CONFIG --version >/dev/null 2>&1; then
+  ICU_INCLUDES=`$ICU_CONFIG --cppflags`
+  ICU_LIBS=`$ICU_CONFIG --ldflags-searchpath --ldflags-libsonly --ldflags-system`
+elif $PKG_CONFIG --libs icu-uc icu-io >/dev/null 2>&1; then
+  ICU_INCLUDES=`$PKG_CONFIG --cflags icu-uc icu-io`
+  ICU_LIBS=`$PKG_CONFIG --libs icu-uc icu-io`
+elif test "x$need_icu:$with_system_icu" = xyes:yes; then
+  as_fn_error $? "did not find either pkg-config or icu-config; one is required for system icu library support" "$LINENO" 5
+fi
+
+if $PKG_CONFIG harfbuzz; then
+  HARFBUZZ_INCLUDES=`$PKG_CONFIG harfbuzz --cflags`
+  HARFBUZZ_LIBS=`$PKG_CONFIG harfbuzz --libs`
+elif test "x$need_harfbuzz:$with_system_harfbuzz" = xyes:yes; then
+  as_fn_error $? "did not find harfbuzz" "$LINENO" 5
+fi
+
+if $PKG_CONFIG luajit; then
+  LUAJIT_INCLUDES=`$PKG_CONFIG luajit --cflags`
+  LUAJIT_LIBS=`$PKG_CONFIG luajit --libs`
+elif test "x$need_luajit:$with_system_luajit" = xyes:yes; then
+  as_fn_error $? "did not find luajit" "$LINENO" 5
 fi
 
-## libs/cairo/ac/cairo.ac: configure.ac fragment for the TeX Live subdirectory libs/cairo/
-## basic check of system cairo
-if test "x$need_cairo:$with_system_cairo" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`cairo' library" >&5
-printf %s "checking requested system \`cairo' library... " >&6; }
-  CPPFLAGS="$CAIRO_INCLUDES $CPPFLAGS"
-  LIBS="$CAIRO_LIBS $LIBS"
+
+
+
+
+
+## texk/kpathsea/ac/kpathsea.ac: configure.ac fragment for the TeX Live subdirectory texk/kpathsea/
+## basic check of system kpathsea
+if test "x$need_kpathsea:$with_system_kpathsea" = xyes:yes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`kpathsea' library" >&5
+printf %s "checking requested system \`kpathsea' library... " >&6; }
+  CPPFLAGS="$KPATHSEA_INCLUDES $CPPFLAGS"
+  LIBS="$KPATHSEA_LIBS $LIBS"
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <cairo.h>
+#include <kpathsea/kpathsea.h>
+#include <kpathsea/version.h>
 int
 main (void)
 {
-const char *s = cairo_version_string();
+const char *version = kpathsea_version_string;
+kpse_set_program_name("prog", "name");
   ;
   return 0;
 }
@@ -25832,20 +23538,22 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 printf "%s\n" "$kpse_res" >&6; }
 fi
 
-## libs/gmp/ac/gmp.ac: configure.ac fragment for the TeX Live subdirectory libs/gmp/
-## basic check of system gmp
-if test "x$need_gmp:$with_system_gmp" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`gmp' library" >&5
-printf %s "checking requested system \`gmp' library... " >&6; }
-  CPPFLAGS="$GMP_INCLUDES $CPPFLAGS"
-  LIBS="$GMP_LIBS $LIBS"
+## libs/zlib/ac/zlib.ac: configure.ac fragment for the TeX Live subdirectory libs/zlib/
+## basic check of system zlib
+if test "x$need_zlib:$with_system_zlib" = xyes:yes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`zlib' library" >&5
+printf %s "checking requested system \`zlib' library... " >&6; }
+  CPPFLAGS="$ZLIB_INCLUDES $CPPFLAGS"
+  LIBS="$ZLIB_LIBS $LIBS"
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <gmp.h>
+#include <zlib.h>
 int
 main (void)
 {
-const char *s = gmp_version;
+z_stream stream;
+const char *version = zlibVersion();
+deflate(&stream, 0);
   ;
   return 0;
 }
@@ -25863,20 +23571,20 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 printf "%s\n" "$kpse_res" >&6; }
 fi
 
-## libs/mpfr/ac/mpfr.ac: configure.ac fragment for the TeX Live subdirectory libs/mpfr/
-## basic check of system mpfr
-if test "x$need_mpfr:$with_system_mpfr" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`mpfr' library" >&5
-printf %s "checking requested system \`mpfr' library... " >&6; }
-  CPPFLAGS="$MPFR_INCLUDES $CPPFLAGS"
-  LIBS="$MPFR_LIBS $LIBS"
+## libs/luajit/ac/luajit.ac: configure.ac fragment for the TeX Live subdirectory libs/luajit/
+## basic check of system luajit
+if test "x$need_luajit:$with_system_luajit" = xyes:yes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`luajit' library" >&5
+printf %s "checking requested system \`luajit' library... " >&6; }
+  CPPFLAGS="$LUAJIT_INCLUDES $CPPFLAGS"
+  LIBS="$LUAJIT_LIBS $LIBS"
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <mpfr.h>
+#include <luajit.h>
 int
 main (void)
 {
-const char *s = mpfr_get_version();
+const char *v = LUAJIT_VERSION;
   ;
   return 0;
 }
@@ -25894,31 +23602,26 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 printf "%s\n" "$kpse_res" >&6; }
 fi
 
-## libs/xpdf/ac/xpdf.ac: configure.ac fragment for the TeX Live subdirectory libs/xpdf/
-## basic check of system xpdf (a.k.a. poppler, no longer supported in
-## TL sources)
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-if test "x$need_xpdf:$with_system_xpdf" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`xpdf' library" >&5
-printf %s "checking requested system \`xpdf' library... " >&6; }
-  CPPFLAGS="$XPDF_INCLUDES $CPPFLAGS"
-  LIBS="$XPDF_LIBS $LIBS"
+## libs/libpng/ac/libpng.ac: configure.ac fragment for the TeX Live subdirectory libs/libpng/
+## basic check of system libpng
+if test "x$need_libpng:$with_system_libpng" = xyes:yes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`libpng' library" >&5
+printf %s "checking requested system \`libpng' library... " >&6; }
+  CPPFLAGS="$LIBPNG_INCLUDES $CPPFLAGS"
+  LIBS="$LIBPNG_LIBS $LIBS"
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <GfxFont.h>
+#include <png.h>
 int
 main (void)
 {
-GfxFont *gfxFont; gfxFont->isOk();
+png_structp png; png_voidp io; png_rw_ptr fn;
+png_set_read_fn(png, io, fn);
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_cxx_try_link "$LINENO"
+if ac_fn_c_try_link "$LINENO"
 then :
   syslib_used=yes kpse_res=ok
 else case e in #(
@@ -25930,11 +23633,6 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
 printf "%s\n" "$kpse_res" >&6; }
 fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 ## libs/zziplib/ac/zziplib.ac: configure.ac fragment for the TeX Live subdirectory libs/zziplib/
 ## basic check of system zziplib
@@ -25999,89 +23697,6 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 printf "%s\n" "$kpse_res" >&6; }
 fi
 
-## libs/teckit/ac/teckit.ac: configure.ac fragment for the TeX Live subdirectory libs/teckit/
-## basic check of system teckit
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-if test "x$need_teckit:$with_system_teckit" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`teckit' library" >&5
-printf %s "checking requested system \`teckit' library... " >&6; }
-  CPPFLAGS="$TECKIT_INCLUDES $CPPFLAGS"
-  LIBS="$TECKIT_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <teckit/TECkit_Engine.h>
-int
-main (void)
-{
-TECkit_Converter converter; TECkit_DisposeConverter(converter);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
-esac
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
-fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-## libs/icu/ac/icu.ac: configure.ac fragment for the TeX Live subdirectory libs/icu/
-## basic check of system icu
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-if test "x$need_icu:$with_system_icu" = xyes:yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking requested system \`icu' library" >&5
-printf %s "checking requested system \`icu' library... " >&6; }
-  CPPFLAGS="$ICU_INCLUDES $CPPFLAGS"
-  LIBS="$ICU_LIBS $LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <unicode/ustring.h>
-int
-main (void)
-{
-UErrorCode code; const UChar *src; UChar *dst;
-u_strToLower(dst, -1, src, -1, NULL, &code);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_link "$LINENO"
-then :
-  syslib_used=yes kpse_res=ok
-else case e in #(
-  e) syslib_status=no kpse_res=failed ;;
-esac
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $kpse_res" >&5
-printf "%s\n" "$kpse_res" >&6; }
-fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
 ## libs/harfbuzz/ac/harfbuzz.ac: configure.ac fragment for the TeX Live subdirectory libs/harfbuzz/
 ## basic check of system harfbuzz
 if test "x$need_harfbuzz:$with_system_harfbuzz" = xyes:yes; then
@@ -26733,7 +24348,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 TeX Live $as_me 2024-03-10, which was
+This file was extended by TeX Live $as_me 2025-03-07, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -26792,7 +24407,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="\\
-TeX Live config.status 2024-03-10
+TeX Live config.status 2025-03-07
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
@@ -28759,11 +26374,11 @@ msg_compiling="$msg_compiling
 test "x$srcdir" = x. || msg_compiling="$msg_compiling
       from sources in $kpse_src"
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result:
-** Configuration summary for $PACKAGE_STRING (2025$with_banner_add):
+** Configuration summary for $PACKAGE_STRING (2026/dev$with_banner_add):
 
    $msg_compiling" >&5
 printf "%s\n" "
-** Configuration summary for $PACKAGE_STRING (2025$with_banner_add):
+** Configuration summary for $PACKAGE_STRING (2026/dev$with_banner_add):
 
    $msg_compiling" >&6; }
 case $kpse_src$kpse_bld in
diff --git a/source/libs/README b/source/libs/README
index 8a661b213..be663eb9b 100644
--- a/source/libs/README
+++ b/source/libs/README
@@ -1,4 +1,4 @@
-$Id: README 74131 2025-02-19 04:34:49Z kakuto $
+$Id: README 74540 2025-03-08 22:22:06Z kakuto $
 Public domain.  Originally created by Karl Berry, 2005.
 
 Libraries we compile for TeX Live.
@@ -28,7 +28,7 @@ graphite2 1.3.14 - checked 10apr20
   https://sourceforge.net/projects/silgraphite/files/graphite2/
   (requires C++11)
 
-harfbuzz 10.2.0 - checked 12jan25
+harfbuzz 10.4.0 - checked 09mar25
   https://github.com/harfbuzz/harfbuzz/releases/latest
 
 icu 76.1 - checked 27oct24 (requires C++17, e.g., g++13)
@@ -37,7 +37,7 @@ icu 76.1 - checked 27oct24 (requires C++17, e.g., g++13)
 libpaper 1.1.29 - checked 07jan24
   https://ftp.debian.org/debian/pool/main/libp/libpaper/
 
-libpng 1.6.46 - checked 25jan25
+libpng 1.6.47 - checked 09mar25
   https://sourceforge.net/projects/libpng/files/ - used by many
 
 lua 5.3.6 - checked 04oct20
diff --git a/source/libs/configure b/source/libs/configure
index 4bcbb6e01..43447b388 100755
--- a/source/libs/configure
+++ b/source/libs/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for TeX Live libs 2025.
+# Generated by GNU Autoconf 2.72 for TeX Live libs 2026/dev.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -603,8 +603,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='TeX Live libs'
 PACKAGE_TARNAME='tex-live-libs'
-PACKAGE_VERSION='2025'
-PACKAGE_STRING='TeX Live libs 2025'
+PACKAGE_VERSION='2026/dev'
+PACKAGE_STRING='TeX Live libs 2026/dev'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1382,7 +1382,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 TeX Live libs 2025 to adapt to many kinds of systems.
+'configure' configures TeX Live libs 2026/dev to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1453,7 +1453,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of TeX Live libs 2025:";;
+     short | recursive ) echo "Configuration of TeX Live libs 2026/dev:";;
    esac
   cat <<\_ACEOF
 
@@ -1639,7 +1639,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-TeX Live libs configure 2025
+TeX Live libs configure 2026/dev
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -1799,7 +1799,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 TeX Live libs $as_me 2025, which was
+It was created by TeX Live libs $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -5776,7 +5776,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='tex-live-libs'
- VERSION='2025'
+ VERSION='2026/dev'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -6737,7 +6737,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 TeX Live libs $as_me 2025, which was
+This file was extended by TeX Live libs $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6796,7 +6796,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="\\
-TeX Live libs config.status 2025
+TeX Live libs config.status 2026/dev
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/libs/harfbuzz/ChangeLog b/source/libs/harfbuzz/ChangeLog
index a4a3938e1..49a13b5b4 100644
--- a/source/libs/harfbuzz/ChangeLog
+++ b/source/libs/harfbuzz/ChangeLog
@@ -1,3 +1,8 @@
+2025-03-09  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Import harfbuzz-10.4.0.
+	* version.ac: Adjusted.
+
 2025-01-12  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
 
 	Import harfbuzz-10.2.0.
diff --git a/source/libs/harfbuzz/TLpatches/ChangeLog b/source/libs/harfbuzz/TLpatches/ChangeLog
index 3f77b7feb..32f6c06d4 100644
--- a/source/libs/harfbuzz/TLpatches/ChangeLog
+++ b/source/libs/harfbuzz/TLpatches/ChangeLog
@@ -1,3 +1,8 @@
+2025-03-09  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Imported harfbuzz-10.4.0 source tree from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/10.4.0/
+
 2025-01-12  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
 
 	Imported harfbuzz-10.2.0 source tree from:
diff --git a/source/libs/harfbuzz/TLpatches/TL-Changes b/source/libs/harfbuzz/TLpatches/TL-Changes
index ccbddc86b..91b5aa943 100644
--- a/source/libs/harfbuzz/TLpatches/TL-Changes
+++ b/source/libs/harfbuzz/TLpatches/TL-Changes
@@ -1,5 +1,5 @@
-Changes applied to the harfbuzz-10.2.0/ tree as obtained from:
-	https://github.com/harfbuzz/harfbuzz/releases/download/10.2.0/
+Changes applied to the harfbuzz-10.4.0/ tree as obtained from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/10.4.0/
 
 Removed:
 	.clang-format
diff --git a/source/libs/harfbuzz/configure b/source/libs/harfbuzz/configure
index 746df925c..43d9ee0e2 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.72 for harfbuzz (TeX Live) 10.2.0.
+# Generated by GNU Autoconf 2.72 for harfbuzz (TeX Live) 10.4.0.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -604,8 +604,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='harfbuzz (TeX Live)'
 PACKAGE_TARNAME='harfbuzz--tex-live-'
-PACKAGE_VERSION='10.2.0'
-PACKAGE_STRING='harfbuzz (TeX Live) 10.2.0'
+PACKAGE_VERSION='10.4.0'
+PACKAGE_STRING='harfbuzz (TeX Live) 10.4.0'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1341,7 +1341,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) 10.2.0 to adapt to many kinds of systems.
+'configure' configures harfbuzz (TeX Live) 10.4.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1413,7 +1413,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of harfbuzz (TeX Live) 10.2.0:";;
+     short | recursive ) echo "Configuration of harfbuzz (TeX Live) 10.4.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1518,7 +1518,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-harfbuzz (TeX Live) configure 10.2.0
+harfbuzz (TeX Live) configure 10.4.0
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -2075,7 +2075,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 10.2.0, which was
+It was created by harfbuzz (TeX Live) $as_me 10.4.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -5252,7 +5252,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='harfbuzz--tex-live-'
- VERSION='10.2.0'
+ VERSION='10.4.0'
 
 
 # Some tools Automake needs.
@@ -5441,9 +5441,9 @@ echo 'tldbg:KPSE_BASIC done (pkg=harfbuzz, amopt=no-define)' >&5
 
 
 HB_VERSION_MAJOR=10
-HB_VERSION_MINOR=2
+HB_VERSION_MINOR=4
 HB_VERSION_MICRO=0
-HB_VERSION=10.2.0
+HB_VERSION=10.4.0
 
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
@@ -9292,7 +9292,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 10.2.0, which was
+This file was extended by harfbuzz (TeX Live) $as_me 10.4.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -9360,7 +9360,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 10.2.0
+harfbuzz (TeX Live) config.status 10.4.0
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/CMakeLists.txt b/source/libs/harfbuzz/harfbuzz-src/CMakeLists.txt
index c3e568fe3..3b4f5aa61 100644
--- a/source/libs/harfbuzz/harfbuzz-src/CMakeLists.txt
+++ b/source/libs/harfbuzz/harfbuzz-src/CMakeLists.txt
@@ -334,15 +334,15 @@ endif ()
 if (WIN32 AND HB_HAVE_GDI)
   add_definitions(-DHAVE_GDI)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-gdi.h)
-  list(APPEND THIRD_PARTY_LIBS gdi32)
-  list(APPEND PC_LIBS_PRIV -lgdi32)
+  list(APPEND THIRD_PARTY_LIBS gdi32 user32)
+  list(APPEND PC_LIBS_PRIV -lgdi32 -luser32)
 endif ()
 
 if (WIN32 AND HB_HAVE_UNISCRIBE)
   add_definitions(-DHAVE_UNISCRIBE)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.h)
-  list(APPEND THIRD_PARTY_LIBS usp10 gdi32 rpcrt4)
-  list(APPEND PC_LIBS_PRIV -lusp10 -lgdi32 -lrpcrt4)
+  list(APPEND THIRD_PARTY_LIBS usp10 gdi32 rpcrt4 user32)
+  list(APPEND PC_LIBS_PRIV -lusp10 -lgdi32 -lrpcrt4 -luser32)
 endif ()
 
 if (WIN32 AND HB_HAVE_DIRECTWRITE)
diff --git a/source/libs/harfbuzz/harfbuzz-src/NEWS b/source/libs/harfbuzz/harfbuzz-src/NEWS
index ce123f2a5..4e7dbe0d9 100644
--- a/source/libs/harfbuzz/harfbuzz-src/NEWS
+++ b/source/libs/harfbuzz/harfbuzz-src/NEWS
@@ -1,3 +1,65 @@
+Overview of changes leading to 10.4.0
+Saturday, March 1, 2025
+====================================
+- Drawing glyphs using hb-draw API now avoids any “malloc” calls, which
+  improves drawing performance by 10+%.
+- Add support new “GVAR” table fonts with more than 65535 glyphs. Support is
+  currently behind a compilation flag and is disabled by default.
+- Some hb-directwrite and hb-ft APIs got renamed with more clear names and the
+  old names are deprecated.
+- Various build and fuzzing fixes.
+
+- New API:
++hb_directwrite_face_get_dw_font_face()
++hb_ft_font_get_ft_face()
+
+- Deprecated API:
++hb_directwrite_face_get_font_face()
++hb_ft_font_get_face()
+
+
+Overview of changes leading to 10.3.0
+Thursday, February 11, 2025
+====================================
+- Vastly improved “AAT” shaping performance. LucidaGrande benchmark-shape
+  before: 14.6ms after: 5.9ms.
+- Improved OpenType shaping performance (kerning / ligature), at the expense of
+  ~1kb per face allocated cache memory. Roboto-Regular benchmark-shape before:
+  10.3ms after: 9.4ms.
+- Improved “COLRv1” benchmark-font paint performance. Before: 7.85ms after
+  4.85ms.
+- Don’t apply glyph substitutions in “morx” table of a font with known broken
+  “morx” table (AALMAGHRIBI.ttf font).
+- Update IANA and OT language registries.
+- Various documentation updates.
+- Various build improvements, and test speed-ups.
+- The “hb_face_reference_blob()” API now works for faces created with
+  “hb_face_create_for_tables()” if the face sets “get_table_tags” callback.
+  This constructs a new face blob from individual table blobs.
+- Various fixes to how “trak” table is handled to bring it closer to Core Text
+  behaviour. Particularly, the tracking values for sizes not explicitly set in
+  the table are now properly interpolated, and the tracking is applied to glyph
+  advances when they are returned by ot-font functions, instead of applying
+  them during shaping. The “trak” pseudo OpenType feature that could be used to
+  disable “trak” table application have been dropped.
+- Core Text font functions now support non-BMP code points.
+- The drawing algorithm used by hb-draw for “glyf” table now match the
+  algorithm used by FreeType and Core Text.
+- The “hb_coretext_font_create()” API now copy font variations from Core Text
+  font to the created HarfBuzz font.
+- Add an API to get the feature tags enabled on a given shape-plan after
+  executing it, which can be used to applications to show in the UI what
+  features are applied by default (which can vary based on the font, the
+  script, the language, and the direction set on the buffer).
+- Add APIs to created HarfBuzz font from DirectWrite font, and copy the font
+  variations.
+
+- New API:
++hb_directwrite_font_create()
++hb_directwrite_font_get_dw_font()
++hb_ot_shape_plan_get_feature_tags()
+
+
 Overview of changes leading to 10.2.0
 Saturday, January 11, 2025
 ====================================
@@ -183,10 +245,10 @@ Saturday, November 11, 2023
   tools. Old option is kept as an alias.
 
 - New API:
-HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION
++HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION
 
 - Deprecated API:
-HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION
++HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION
 
 Overview of changes leading to 8.2.2
 Wednesday, October 18, 2023
diff --git a/source/libs/harfbuzz/harfbuzz-src/README.md b/source/libs/harfbuzz/harfbuzz-src/README.md
index 2cd8b4068..02c1554bd 100644
--- a/source/libs/harfbuzz/harfbuzz-src/README.md
+++ b/source/libs/harfbuzz/harfbuzz-src/README.md
@@ -1,9 +1,9 @@
-[![Linux CI Status](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg)](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg)
+[![Linux CI Status](https://github.com/harfbuzz/harfbuzz/actions/workflows/linux-ci.yml/badge.svg)](https://github.com/harfbuzz/harfbuzz/actions/workflows/linux-ci.yml)
+[![macoOS CI Status](https://github.com/harfbuzz/harfbuzz/actions/workflows/macos-ci.yml/badge.svg)](https://github.com/harfbuzz/harfbuzz/actions/workflows/macos-ci.yml)
+[![Windows CI Status](https://github.com/harfbuzz/harfbuzz/actions/workflows/msvc-ci.yml/badge.svg)](https://github.com/harfbuzz/harfbuzz/actions/workflows/msvc-ci.yml)
 [![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://app.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=gh&utm_medium=referral&utm_content=&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)
 [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz)
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/meson.build b/source/libs/harfbuzz/harfbuzz-src/meson.build
index f3e43b595..1460c2a9d 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: '10.2.0',
+  version: '10.4.0',
   default_options: [
     'cpp_eh=none',          # Just to support msvc, we are passing -fno-exceptions also anyway
     # 'cpp_rtti=false',     # Do NOT enable, wraps inherit it and ICU needs RTTI
@@ -321,7 +321,7 @@ if host_machine.system() == 'windows' and not get_option('gdi').disabled()
   endif
 
   gdi_deps_found = true
-  foreach usplib : ['usp10', 'gdi32', 'rpcrt4']
+  foreach usplib : ['usp10', 'gdi32', 'rpcrt4', 'user32']
     dep = cpp.find_library(usplib, required: get_option('gdi'))
     gdi_deps_found = gdi_deps_found and dep.found()
     gdi_uniscribe_deps += dep
@@ -464,6 +464,13 @@ configure_file(output: 'config.h', configuration: conf)
 alias_target('lib', libharfbuzz)
 alias_target('libs', libharfbuzz, libharfbuzz_subset)
 
+if meson.version().version_compare('>=0.57.0')
+  # Re glib, see https://github.com/harfbuzz/harfbuzz/issues/4153#issuecomment-2646347531
+  add_test_setup('default',
+                 exclude_suites: ['google-benchmark'],
+                 is_default: glib_dep.type_name() != 'internal' and not meson.is_subproject())
+endif
+
 build_summary = {
   'Directories':
     {'prefix': get_option('prefix'),
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CBDT/CBDT.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CBDT/CBDT.hh
index bcf1848f4..50550e811 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CBDT/CBDT.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CBDT/CBDT.hh
@@ -941,10 +941,12 @@ struct CBDT
       }
     }
 
-    bool has_data () const { return cbdt.get_length (); }
+    bool has_data () const { return cbdt->version.major; }
 
     bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
     {
+      if (!has_data ()) return false;
+
       hb_glyph_extents_t extents;
       hb_glyph_extents_t pixel_extents;
       hb_blob_t *blob = reference_png (font, glyph);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/COLR/COLR.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/COLR/COLR.hh
index d227768d5..cf10e894a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/COLR/COLR.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/COLR/COLR.hh
@@ -29,11 +29,14 @@
 #define OT_COLOR_COLR_COLR_HH
 
 #include "../../../hb.hh"
+#include "../../../hb-decycler.hh"
 #include "../../../hb-open-type.hh"
 #include "../../../hb-ot-var-common.hh"
 #include "../../../hb-paint.hh"
 #include "../../../hb-paint-extents.hh"
 
+#include "../CPAL/CPAL.hh"
+
 /*
  * COLR -- Color
  * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
@@ -66,11 +69,11 @@ public:
   hb_paint_funcs_t *funcs;
   void *data;
   hb_font_t *font;
-  unsigned int palette_index;
+  hb_array_t<const BGRAColor> palette;
   hb_color_t foreground;
   ItemVarStoreInstancer &instancer;
-  hb_map_t current_glyphs;
-  hb_map_t current_layers;
+  hb_decycler_t glyphs_decycler;
+  hb_decycler_t layers_decycler;
   int depth_left = HB_MAX_NESTING_LEVEL;
   int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
 
@@ -85,7 +88,11 @@ public:
     funcs (funcs_),
     data (data_),
     font (font_),
-    palette_index (palette_),
+    palette (
+#ifndef HB_NO_COLOR
+	     font->face->table.CPAL->get_palette_colors (palette_)
+#endif
+    ),
     foreground (foreground_),
     instancer (instancer_)
   { }
@@ -99,12 +106,7 @@ public:
     if (color_index != 0xffff)
     {
       if (!funcs->custom_palette_color (data, color_index, &color))
-      {
-	unsigned int clen = 1;
-	hb_face_t *face = hb_font_get_face (font);
-
-	hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
-      }
+        color = palette[color_index];
 
       *is_foreground = false;
     }
@@ -2134,12 +2136,16 @@ struct COLR
 
     const ItemVariationStore &get_var_store () const
     { return colr->get_var_store (); }
+    const ItemVariationStore *get_var_store_ptr () const
+    { return colr->get_var_store_ptr (); }
 
     bool has_delta_set_index_map () const
     { return colr->has_delta_set_index_map (); }
 
     const DeltaSetIndexMap &get_delta_set_index_map () const
     { return colr->get_delta_set_index_map (); }
+    const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+    { return colr->get_delta_set_index_map_ptr (); }
 
     private:
     hb_blob_ptr_t<COLR> colr;
@@ -2232,9 +2238,13 @@ struct COLR
 
   const DeltaSetIndexMap &get_delta_set_index_map () const
   { return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }
+  const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+  { return has_delta_set_index_map () && hb_barrier () ? &(this+varIdxMap) : nullptr; }
 
   const ItemVariationStore &get_var_store () const
   { return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }
+  const ItemVariationStore *get_var_store_ptr () const
+  { return has_var_store () && hb_barrier () ? &(this+varStore) : nullptr; }
 
   const ClipList &get_clip_list () const
   { return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }
@@ -2482,9 +2492,9 @@ struct COLR
      * after instancing */
     if (!subset_varstore (c, colr_prime)) return_trace (false);
 
-    ItemVarStoreInstancer instancer (&(get_var_store ()),
-	                         &(get_delta_set_index_map ()),
-	                         c->plan->normalized_coords.as_array ());
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+				     get_delta_set_index_map_ptr (),
+				     c->plan->normalized_coords.as_array ());
 
     if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
       return_trace (false);
@@ -2513,8 +2523,8 @@ struct COLR
   get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
   {
 
-    ItemVarStoreInstancer instancer (&(get_var_store ()),
-                                     &(get_delta_set_index_map ()),
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+                                     get_delta_set_index_map_ptr (),
                                      hb_array (font->coords, font->num_coords));
 
     if (get_clip (glyph, extents, instancer))
@@ -2575,11 +2585,13 @@ struct COLR
   bool
   paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
   {
-    ItemVarStoreInstancer instancer (&(get_var_store ()),
-	                         &(get_delta_set_index_map ()),
-	                         hb_array (font->coords, font->num_coords));
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+				     get_delta_set_index_map_ptr (),
+				     hb_array (font->coords, font->num_coords));
     hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
-    c.current_glyphs.add (glyph);
+
+    hb_decycler_node_t node (c.glyphs_decycler);
+    node.visit (glyph);
 
     if (version >= 1)
     {
@@ -2695,19 +2707,16 @@ void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
 {
   TRACE_PAINT (this);
   const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
+  hb_decycler_node_t node (c->layers_decycler);
   for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
   {
-    if (unlikely (c->current_layers.has (i)))
-      continue;
-
-    c->current_layers.add (i);
+    if (unlikely (!node.visit (i)))
+      return;
 
     const Paint &paint = paint_offset_lists.get_paint (i);
     c->funcs->push_group (c->data);
     c->recurse (paint);
     c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
-
-    c->current_layers.del (i);
   }
 }
 
@@ -2715,16 +2724,14 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
 {
   TRACE_PAINT (this);
 
-  if (unlikely (c->current_glyphs.has (gid)))
+  hb_decycler_node_t node (c->glyphs_decycler);
+  if (unlikely (!node.visit (gid)))
     return;
 
-  c->current_glyphs.add (gid);
-
   c->funcs->push_inverse_root_transform (c->data, c->font);
   if (c->funcs->color_glyph (c->data, gid, c->font))
   {
     c->funcs->pop_transform (c->data);
-    c->current_glyphs.del (gid);
     return;
   }
   c->funcs->pop_transform (c->data);
@@ -2747,8 +2754,6 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
 
   if (has_clip_box)
     c->funcs->pop_clip (c->data);
-
-  c->current_glyphs.del (gid);
 }
 
 } /* namespace OT */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CPAL/CPAL.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CPAL/CPAL.hh
index 2821334db..940126a86 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CPAL/CPAL.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Color/CPAL/CPAL.hh
@@ -187,6 +187,14 @@ struct CPAL
   hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
   { return v1 ().get_color_name_id (this, color_index, numColors); }
 
+  hb_array_t<const BGRAColor> get_palette_colors (unsigned int palette_index) const
+  {
+    if (unlikely (palette_index >= numPalettes))
+      return hb_array_t<const BGRAColor> ();
+    unsigned int start_index = colorRecordIndicesZ[palette_index];
+    hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
+    return all_colors.sub_array (start_index, numColors);
+  }
   unsigned int get_palette_colors (unsigned int  palette_index,
 				   unsigned int  start_offset,
 				   unsigned int *color_count, /* IN/OUT.  May be NULL. */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/Coverage.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/Coverage.hh
index 344e87afb..daa197954 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/Coverage.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/Coverage.hh
@@ -96,6 +96,15 @@ struct Coverage
     default:return NOT_COVERED;
     }
   }
+  unsigned int get_coverage (hb_codepoint_t glyph_id,
+			     hb_ot_lookup_cache_t *cache) const
+  {
+    unsigned coverage;
+    if (cache && cache->get (glyph_id, &coverage)) return coverage;
+    coverage = get_coverage (glyph_id);
+    if (cache) cache->set (glyph_id, coverage);
+    return coverage;
+  }
 
   unsigned get_population () const
   {
@@ -201,6 +210,19 @@ struct Coverage
     }
   }
 
+  unsigned cost () const
+  {
+    switch (u.format) {
+    case 1: hb_barrier (); return u.format1.cost ();
+    case 2: hb_barrier (); return u.format2.cost ();
+#ifndef HB_NO_BEYOND_64K
+    case 3: hb_barrier (); return u.format3.cost ();
+    case 4: hb_barrier (); return u.format4.cost ();
+#endif
+    default:return 0u;
+    }
+  }
+
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
   template <typename set_t>
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat1.hh
index 3f598d40e..cdba7a505 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat1.hh
@@ -103,6 +103,8 @@ struct CoverageFormat1_3
         intersect_glyphs << glyphArray[i];
   }
 
+  unsigned cost () const { return hb_bit_storage ((unsigned) glyphArray.len); /* bsearch cost */ }
+
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
   { return glyphs->add_sorted_array (glyphArray.as_array ()); }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat2.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat2.hh
index 9c8754235..dd577fd90 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat2.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat2.hh
@@ -157,6 +157,8 @@ struct CoverageFormat2_4
     }
   }
 
+  unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ }
+
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
   {
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
index ac2774a76..de67548c4 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat1.hh
@@ -103,12 +103,50 @@ struct PairPosFormat1_3
 
   const Coverage &get_coverage () const { return this+coverage; }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
+  {
+    return (this+coverage).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
+	if (likely (cache))
+	  cache->clear ();
+	return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+	return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
+	hb_free (cache);
+	return nullptr;
+      }
+    }
+    return nullptr;
+  }
+
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
   {
     TRACE_APPLY (this);
+
     hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache);
+#else
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset_fast (buffer->idx);
@@ -156,7 +194,7 @@ struct PairPosFormat1_3
         strip = true;
       newFormats = compute_effective_value_formats (glyphset, strip, true);
     }
-    
+
     out->valueFormat[0] = newFormats.first;
     out->valueFormat[1] = newFormats.second;
 
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
index 5ffeb5d0c..f30148ab6 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat2.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairPosFormat2.hh
@@ -123,12 +123,61 @@ struct PairPosFormat2_4 : ValueBase
 
   const Coverage &get_coverage () const { return this+coverage; }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  struct pair_pos_cache_t
+  {
+    hb_ot_lookup_cache_t coverage;
+    hb_ot_lookup_cache_t first;
+    hb_ot_lookup_cache_t second;
+  };
+
+  unsigned cache_cost () const
+  {
+    return (this+coverage).cost () + (this+classDef1).cost () + (this+classDef2).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+	pair_pos_cache_t *cache = (pair_pos_cache_t *) hb_malloc (sizeof (pair_pos_cache_t));
+	if (likely (cache))
+	{
+	  cache->coverage.clear ();
+	  cache->first.clear ();
+	  cache->second.clear ();
+	}
+	return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+	return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+	{
+	  pair_pos_cache_t *cache = (pair_pos_cache_t *) p;
+	  hb_free (cache);
+	  return nullptr;
+	}
+    }
+    return nullptr;
+  }
+
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
   {
     TRACE_APPLY (this);
+
     hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    pair_pos_cache_t *cache = cached ? (pair_pos_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
+#else
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset_fast (buffer->idx);
@@ -139,8 +188,13 @@ struct PairPosFormat2_4 : ValueBase
       return_trace (false);
     }
 
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint, cache ? &cache->first : nullptr);
+    unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint, cache ? &cache->second : nullptr);
+#else
     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
+#endif
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
     {
       buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
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
index b2d151d44..e9fe39231 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat1.hh
@@ -67,7 +67,7 @@ struct SinglePosFormat1 : ValueBase
     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 (index == NOT_COVERED) return_trace (false);
 
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
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
index ae4a5ed75..2fb710589 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat2.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/SinglePosFormat2.hh
@@ -66,7 +66,7 @@ struct SinglePosFormat2 : ValueBase
     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 (index == NOT_COVERED) return_trace (false);
 
     if (unlikely (index >= valueCount)) return_trace (false);
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/AlternateSubstFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
index adec65d58..421a6e066 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
@@ -74,7 +74,7 @@ struct AlternateSubstFormat1_2
     TRACE_APPLY (this);
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     return_trace ((this+alternateSet[index]).apply (c));
   }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
index 5c7df97d1..2ef46145e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
@@ -78,12 +78,49 @@ struct LigatureSubstFormat1_2
     return lig_set.would_apply (c);
   }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
   {
-    TRACE_APPLY (this);
+    return (this+coverage).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
+	if (likely (cache))
+	  cache->clear ();
+	return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+	return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
+	hb_free (cache);
+	return nullptr;
+      }
+    }
+    return nullptr;
+  }
 
-    unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache);
+#else
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
     const auto &lig_set = this+ligatureSet[index];
     return_trace (lig_set.apply (c));
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/MultipleSubstFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
index 3b4bd1169..441d4dee0 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
@@ -66,7 +66,7 @@ struct MultipleSubstFormat1_2
     TRACE_APPLY (this);
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     return_trace ((this+sequence[index]).apply (c));
   }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
index ec374f2f0..1f598cc40 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
@@ -112,7 +112,7 @@ struct ReverseChainSingleSubstFormat1
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
       return_trace (false); /* No chaining to this type */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat1.hh
index 850be86c0..550d8f04e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat1.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat1.hh
@@ -128,7 +128,7 @@ struct SingleSubstFormat1_3
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage).get_coverage (glyph_id);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     hb_codepoint_t d = deltaGlyphID;
     hb_codepoint_t mask = get_mask ();
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat2.hh
index 9c651abe7..dce28b672 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat2.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GSUB/SingleSubstFormat2.hh
@@ -104,7 +104,7 @@ struct SingleSubstFormat2_4
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     if (unlikely (index >= substitute.len)) return_trace (false);
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/types.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/types.hh
index 3840db059..527f64114 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/types.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/types.hh
@@ -29,6 +29,9 @@
 #ifndef OT_LAYOUT_TYPES_HH
 #define OT_LAYOUT_TYPES_HH
 
+using hb_ot_lookup_cache_t = hb_cache_t<15, 8, 7>;
+static_assert (sizeof (hb_ot_lookup_cache_t) == 256, "");
+
 namespace OT {
 namespace Layout {
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.cc b/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.cc
index 1afb57111..3b9eec4fb 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.cc
@@ -3,7 +3,6 @@
 #ifndef HB_NO_VAR_COMPOSITES
 
 #include "../../../hb-draw.hh"
-#include "../../../hb-geometry.hh"
 #include "../../../hb-ot-layout-common.hh"
 #include "../../../hb-ot-layout-gdef-table.hh"
 
@@ -133,18 +132,19 @@ VarComponent::get_path_at (hb_font_t *font,
 			   hb_codepoint_t parent_gid,
 			   hb_draw_session_t &draw_session,
 			   hb_array_t<const int> coords,
+			   hb_transform_t total_transform,
 			   hb_ubytes_t total_record,
-			   hb_set_t *visited,
+			   hb_decycler_t *decycler,
 			   signed *edges_left,
 			   signed depth_left,
+			   hb_glyf_scratch_t &scratch,
 			   VarRegionList::cache_t *cache) const
 {
   const unsigned char *end = total_record.arrayZ + total_record.length;
   const unsigned char *record = total_record.arrayZ;
 
-  auto &VARC = *font->face->table.VARC;
+  auto &VARC = *font->face->table.VARC->table;
   auto &varStore = &VARC+VARC.varStore;
-  auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
 
 #define READ_UINT32VAR(name) \
   HB_STMT_START { \
@@ -187,22 +187,25 @@ VarComponent::get_path_at (hb_font_t *font,
     unsigned conditionIndex;
     READ_UINT32VAR (conditionIndex);
     const auto &condition = (&VARC+VARC.conditionList)[conditionIndex];
+    auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
     show = condition.evaluate (coords.arrayZ, coords.length, &instancer);
   }
 
   // Axis values
 
-  hb_vector_t<unsigned> axisIndices;
-  hb_vector_t<float> axisValues;
+  auto &axisIndices = scratch.axisIndices;
+  axisIndices.clear ();
+  auto &axisValues = scratch.axisValues;
+  axisValues.clear ();
   if (flags & (unsigned) flags_t::HAVE_AXES)
   {
     unsigned axisIndicesIndex;
     READ_UINT32VAR (axisIndicesIndex);
-    axisIndices = (&VARC+VARC.axisIndicesList)[axisIndicesIndex];
+    axisIndices.extend ((&VARC+VARC.axisIndicesList)[axisIndicesIndex]);
     axisValues.resize (axisIndices.length);
     const HBUINT8 *p = (const HBUINT8 *) record;
     TupleValues::decompile (p, axisValues, (const HBUINT8 *) end);
-    record += (const unsigned char *) p - record;
+    record = (const unsigned char *) p;
   }
 
   // Apply variations if any
@@ -312,32 +315,87 @@ VarComponent::get_path_at (hb_font_t *font,
     if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
       transform.scaleY = transform.scaleX;
 
-    // Scale the transform by the font's scale
-    float x_scale = font->x_multf;
-    float y_scale = font->y_multf;
-    transform.translateX *= x_scale;
-    transform.translateY *= y_scale;
-    transform.tCenterX *= x_scale;
-    transform.tCenterY *= y_scale;
+    total_transform.transform (transform.to_transform ());
+    total_transform.scale (font->x_mult ? 1.f / font->x_multf : 0.f,
+			   font->y_mult ? 1.f / font->y_multf : 0.f);
 
+    VARC.get_path_at (font, gid,
+		      draw_session, component_coords, total_transform,
+		      parent_gid,
+		      decycler, edges_left, depth_left - 1,
+		      scratch);
+  }
+
+#undef PROCESS_TRANSFORM_COMPONENTS
+#undef READ_UINT32VAR
+
+  return hb_ubytes_t (record, end - record);
+}
+
+bool
+VARC::get_path_at (hb_font_t *font,
+		   hb_codepoint_t glyph,
+		   hb_draw_session_t &draw_session,
+		   hb_array_t<const int> coords,
+		   hb_transform_t transform,
+		   hb_codepoint_t parent_glyph,
+		   hb_decycler_t *decycler,
+		   signed *edges_left,
+		   signed depth_left,
+		   hb_glyf_scratch_t &scratch) const
+{
+  // Don't recurse on the same glyph.
+  unsigned idx = glyph == parent_glyph ?
+		 NOT_COVERED :
+		 (this+coverage).get_coverage (glyph);
+  if (idx == NOT_COVERED)
+  {
     // Build a transforming pen to apply the transform.
     hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
-    hb_transforming_pen_context_t context {transform.to_transform (),
+    hb_transforming_pen_context_t context {transform,
 					   draw_session.funcs,
 					   draw_session.draw_data,
 					   &draw_session.st};
     hb_draw_session_t transformer_session {transformer_funcs, &context};
+    hb_draw_session_t &shape_draw_session = transform.is_identity () ? draw_session : transformer_session;
 
-    VARC.get_path_at (font, gid,
-		      transformer_session, component_coords,
-		      parent_gid,
-		      visited, edges_left, depth_left - 1);
+    if (!font->face->table.glyf->get_path_at (font, glyph, shape_draw_session, coords, scratch))
+#ifndef HB_NO_CFF
+    if (!font->face->table.cff2->get_path_at (font, glyph, shape_draw_session, coords))
+    if (!font->face->table.cff1->get_path (font, glyph, shape_draw_session)) // Doesn't have variations
+#endif
+      return false;
+    return true;
   }
 
-#undef PROCESS_TRANSFORM_COMPONENTS
-#undef READ_UINT32VAR
+  if (depth_left <= 0)
+    return true;
 
-  return hb_ubytes_t (record, end - record);
+  if (*edges_left <= 0)
+    return true;
+  (*edges_left)--;
+
+  hb_decycler_node_t node (*decycler);
+  if (unlikely (!node.visit (glyph)))
+    return true;
+
+  hb_ubytes_t record = (this+glyphRecords)[idx];
+
+  float static_cache[sizeof (void *) * 16];
+  VarRegionList::cache_t *cache = (this+varStore).create_cache (hb_array (static_cache));
+
+  transform.scale (font->x_multf, font->y_multf);
+
+  VarCompositeGlyph::get_path_at (font, glyph,
+				  draw_session, coords, transform,
+				  record,
+				  decycler, edges_left, depth_left,
+				  scratch,
+				  cache);
+
+  (this+varStore).destroy_cache (cache, hb_array (static_cache));
+
+  return true;
 }
 
 //} // namespace Var
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.hh
index d60f7b0c2..30e54b6bd 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/VARC.hh
@@ -1,6 +1,8 @@
 #ifndef OT_VAR_VARC_VARC_HH
 #define OT_VAR_VARC_VARC_HH
 
+#include "../../../hb-decycler.hh"
+#include "../../../hb-geometry.hh"
 #include "../../../hb-ot-layout-common.hh"
 #include "../../../hb-ot-glyf-table.hh"
 #include "../../../hb-ot-cff2-table.hh"
@@ -46,10 +48,12 @@ struct VarComponent
 	       hb_codepoint_t parent_gid,
 	       hb_draw_session_t &draw_session,
 	       hb_array_t<const int> coords,
+	       hb_transform_t transform,
 	       hb_ubytes_t record,
-	       hb_set_t *visited,
+	       hb_decycler_t *decycler,
 	       signed *edges_left,
 	       signed depth_left,
+	       hb_glyf_scratch_t &scratch,
 	       VarRegionList::cache_t *cache = nullptr) const;
 };
 
@@ -60,19 +64,21 @@ struct VarCompositeGlyph
 	       hb_codepoint_t glyph,
 	       hb_draw_session_t &draw_session,
 	       hb_array_t<const int> coords,
+	       hb_transform_t transform,
 	       hb_ubytes_t record,
-	       hb_set_t *visited,
+	       hb_decycler_t *decycler,
 	       signed *edges_left,
 	       signed depth_left,
+	       hb_glyf_scratch_t &scratch,
 	       VarRegionList::cache_t *cache = nullptr)
   {
     while (record)
     {
       const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
       record = comp.get_path_at (font, glyph,
-				 draw_session, coords,
+				 draw_session, coords, transform,
 				 record,
-				 visited, edges_left, depth_left, cache);
+				 decycler, edges_left, depth_left, scratch, cache);
     }
   }
 };
@@ -85,79 +91,37 @@ struct VARC
 
   static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
 
-  bool
+  HB_INTERNAL bool
   get_path_at (hb_font_t *font,
 	       hb_codepoint_t glyph,
 	       hb_draw_session_t &draw_session,
 	       hb_array_t<const int> coords,
-	       hb_codepoint_t parent_glyph = HB_CODEPOINT_INVALID,
-	       hb_set_t *visited = nullptr,
-	       signed *edges_left = nullptr,
-	       signed depth_left = HB_MAX_NESTING_LEVEL) const
-  {
-    hb_set_t stack_set;
-    if (visited == nullptr)
-      visited = &stack_set;
-    signed stack_edges = HB_MAX_GRAPH_EDGE_COUNT;
-    if (edges_left == nullptr)
-      edges_left = &stack_edges;
-
-    // Don't recurse on the same glyph.
-    unsigned idx = glyph == parent_glyph ?
-		   NOT_COVERED :
-		   (this+coverage).get_coverage (glyph);
-    if (idx == NOT_COVERED)
-    {
-      if (!font->face->table.glyf->get_path_at (font, glyph, draw_session, coords))
-#ifndef HB_NO_CFF
-      if (!font->face->table.cff2->get_path_at (font, glyph, draw_session, coords))
-      if (!font->face->table.cff1->get_path (font, glyph, draw_session)) // Doesn't have variations
-#endif
-	return false;
-      return true;
-    }
-
-    if (depth_left <= 0)
-      return true;
-
-    if (*edges_left <= 0)
-      return true;
-    (*edges_left)--;
-
-    if (visited->has (glyph) || visited->in_error ())
-      return true;
-    visited->add (glyph);
-
-    hb_ubytes_t record = (this+glyphRecords)[idx];
-
-    VarRegionList::cache_t *cache = record.length >= 64 ? // Heuristic
-				   (this+varStore).create_cache ()
-				   : nullptr;
-
-    VarCompositeGlyph::get_path_at (font, glyph,
-				    draw_session, coords,
-				    record,
-				    visited, edges_left, depth_left,
-				    cache);
-
-    (this+varStore).destroy_cache (cache);
-
-    visited->del (glyph);
-
-    return true;
-  }
+	       hb_transform_t transform,
+	       hb_codepoint_t parent_glyph,
+	       hb_decycler_t *decycler,
+	       signed *edges_left,
+	       signed depth_left,
+	       hb_glyf_scratch_t &scratch) const;
 
   bool
-  get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
-  { return get_path_at (font, gid, draw_session, hb_array (font->coords, font->num_coords)); }
-
-  bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
+  get_path (hb_font_t *font,
+	    hb_codepoint_t gid,
+	    hb_draw_session_t &draw_session,
+	    hb_glyf_scratch_t &scratch) const
   {
-    funcs->push_clip_glyph (data, gid, font);
-    funcs->color (data, true, foreground);
-    funcs->pop_clip (data);
-
-    return true;
+    hb_decycler_t decycler;
+    signed edges = HB_MAX_GRAPH_EDGE_COUNT;
+
+    return get_path_at (font,
+			gid,
+			draw_session,
+			hb_array (font->coords, font->num_coords),
+			HB_TRANSFORM_IDENTITY,
+			HB_CODEPOINT_INVALID,
+			&decycler,
+			&edges,
+			HB_MAX_NESTING_LEVEL,
+			scratch);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -173,6 +137,63 @@ struct VARC
 		  glyphRecords.sanitize (c, this));
   }
 
+  struct accelerator_t
+  {
+    friend struct VarComponent;
+
+    accelerator_t (hb_face_t *face)
+    {
+      table = hb_sanitize_context_t ().reference_table<VARC> (face);
+    }
+    ~accelerator_t ()
+    {
+      auto *scratch = cached_scratch.get_relaxed ();
+      if (scratch)
+      {
+	scratch->~hb_glyf_scratch_t ();
+	hb_free (scratch);
+      }
+
+      table.destroy ();
+    }
+
+    bool
+    get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
+    {
+      if (!table->has_data ()) return false;
+
+      hb_glyf_scratch_t *scratch;
+
+      // Borrow the cached strach buffer.
+      {
+	scratch = cached_scratch.get_acquire ();
+	if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+	{
+	  scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
+	  if (unlikely (!scratch))
+	    return true;
+	}
+      }
+
+      bool ret = table->get_path (font, gid, draw_session, *scratch);
+
+      // Put it back.
+      if (!cached_scratch.cmpexch (nullptr, scratch))
+      {
+        scratch->~hb_glyf_scratch_t ();
+	hb_free (scratch);
+      }
+
+      return ret;
+    }
+
+    private:
+    hb_blob_ptr_t<VARC> table;
+    hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
+  };
+
+  bool has_data () const { return version.major != 0; }
+
   protected:
   FixedVersion<> version; /* Version identifier */
   Offset32To<Coverage> coverage;
@@ -184,6 +205,10 @@ struct VARC
   DEFINE_SIZE_STATIC (24);
 };
 
+struct VARC_accelerator_t : VARC::accelerator_t {
+  VARC_accelerator_t (hb_face_t *face) : VARC::accelerator_t (face) {}
+};
+
 #endif
 
 //}
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/coord-setter.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/coord-setter.hh
index a2b483ce2..0df524ed2 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/coord-setter.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Var/VARC/coord-setter.hh
@@ -11,22 +11,48 @@ namespace OT {
 
 struct coord_setter_t
 {
-  coord_setter_t (hb_array_t<const int> coords) :
-    coords (coords) {}
+  coord_setter_t (hb_array_t<const int> coords_)
+  {
+    length = coords_.length;
+    if (length <= ARRAY_LENGTH (static_coords))
+      hb_memcpy (static_coords, coords_.arrayZ, length * sizeof (int));
+    else
+      dynamic_coords.extend (coords_);
+  }
 
   int& operator [] (unsigned idx)
   {
     if (unlikely (idx >= HB_VAR_COMPOSITE_MAX_AXES))
       return Crap(int);
-    if (coords.length < idx + 1)
-      coords.resize (idx + 1);
-    return coords[idx];
+
+    if (length <= ARRAY_LENGTH (static_coords))
+    {
+      if (idx < ARRAY_LENGTH (static_coords))
+      {
+        while (length <= idx)
+	  static_coords[length++] = 0;
+	return static_coords[idx];
+      }
+      else
+        dynamic_coords.extend (hb_array (static_coords, length));
+    }
+
+    if (dynamic_coords.length <= idx)
+    {
+      if (unlikely (!dynamic_coords.resize (idx + 1)))
+	return Crap(int);
+      length = idx + 1;
+    }
+    return dynamic_coords.arrayZ[idx];
   }
 
   hb_array_t<int> get_coords ()
-  { return coords.as_array (); }
+  { return length <= ARRAY_LENGTH (static_coords) ? hb_array (static_coords, length) : dynamic_coords.as_array (); }
 
-  hb_vector_t<int> coords;
+  private:
+  hb_vector_t<int> dynamic_coords;
+  unsigned length;
+  int static_coords[sizeof (void *) * 8];
 };
 
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/CompositeGlyph.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/CompositeGlyph.hh
index 5c0ecd513..e2ec4a80e 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/CompositeGlyph.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/CompositeGlyph.hh
@@ -143,7 +143,7 @@ struct CompositeGlyphRecord
     float matrix[4];
     contour_point_t trans;
     get_transformation (matrix, trans);
-    if (unlikely (!points.alloc (points.length + 4))) return false; // For phantom points
+    if (unlikely (!points.alloc (points.length + 1 + 4))) return false; // For phantom points
     points.push (trans);
     return true;
   }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/Glyph.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/Glyph.hh
index 7772597e5..2c289d511 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/Glyph.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/Glyph.hh
@@ -251,7 +251,8 @@ struct Glyph
       composite_contours_p = nullptr;
     }
 
-    if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
+    hb_glyf_scratch_t scratch;
+    if (!get_points (font, glyf, all_points, scratch, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
       return false;
 
     // .notdef, set type to empty so we only update metrics and don't compile bytes for
@@ -305,6 +306,7 @@ struct Glyph
   template <typename accelerator_t>
   bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
 		   contour_point_vector_t &all_points /* OUT */,
+		   hb_glyf_scratch_t &scratch,
 		   contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
 		   head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
 		   unsigned *composite_contours = nullptr, /* OUT */
@@ -312,7 +314,6 @@ struct Glyph
 		   bool use_my_metrics = true,
 		   bool phantom_only = false,
 		   hb_array_t<const int> coords = hb_array_t<const int> (),
-		   hb_map_t *current_glyphs = nullptr,
 		   unsigned int depth = 0,
 		   unsigned *edge_count = nullptr) const
   {
@@ -322,10 +323,6 @@ struct Glyph
     if (unlikely (*edge_count > HB_MAX_GRAPH_EDGE_COUNT)) return false;
     (*edge_count)++;
 
-    hb_map_t current_glyphs_stack;
-    if (current_glyphs == nullptr)
-      current_glyphs = &current_glyphs_stack;
-
     if (head_maxp_info)
     {
       head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
@@ -334,8 +331,7 @@ struct Glyph
     if (!coords)
       coords = hb_array (font->coords, font->num_coords);
 
-    contour_point_vector_t stack_points;
-    contour_point_vector_t &points = type == SIMPLE ? all_points : stack_points;
+    contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
     unsigned old_length = points.length;
 
     switch (type) {
@@ -388,36 +384,53 @@ struct Glyph
 
 #ifndef HB_NO_VAR
     if (coords)
-      glyf_accelerator.gvar->apply_deltas_to_points (gid,
-						     coords,
-						     points.as_array ().sub_array (old_length),
-						     phantom_only && type == SIMPLE);
+    {
+#ifndef HB_NO_BEYOND_64K
+      if (glyf_accelerator.GVAR->has_data ())
+	glyf_accelerator.GVAR->apply_deltas_to_points (gid,
+						       coords,
+						       points.as_array ().sub_array (old_length),
+						       scratch,
+						       phantom_only && type == SIMPLE);
+      else
+#endif
+	glyf_accelerator.gvar->apply_deltas_to_points (gid,
+						       coords,
+						       points.as_array ().sub_array (old_length),
+						       scratch,
+						       phantom_only && type == SIMPLE);
+    }
 #endif
 
     // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
     // with child glyphs' points
     if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
     {
-      if (unlikely (!points_with_deltas->resize (points.length))) return false;
+      assert (old_length == 0);
       *points_with_deltas = points;
     }
 
+    float shift = 0;
     switch (type) {
     case SIMPLE:
       if (depth == 0 && head_maxp_info)
         head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4);
+      shift = phantoms[PHANTOM_LEFT].x;
       break;
     case COMPOSITE:
     {
+      hb_decycler_node_t decycler_node (scratch.decycler);
+
       unsigned int comp_index = 0;
       for (auto &item : get_composite_iterator ())
       {
 	hb_codepoint_t item_gid = item.get_gid ();
 
-        if (unlikely (current_glyphs->has (item_gid)))
+        if (unlikely (!decycler_node.visit (item_gid)))
+	{
+	  comp_index++;
 	  continue;
-
-	current_glyphs->add (item_gid);
+	}
 
 	unsigned old_count = all_points.length;
 
@@ -426,6 +439,7 @@ struct Glyph
 				       .get_points (font,
 						    glyf_accelerator,
 						    all_points,
+						    scratch,
 						    points_with_deltas,
 						    head_maxp_info,
 						    composite_contours,
@@ -433,14 +447,16 @@ struct Glyph
 						    use_my_metrics,
 						    phantom_only,
 						    coords,
-						    current_glyphs,
 						    depth + 1,
 						    edge_count)))
 	{
-	  current_glyphs->del (item_gid);
+	  points.resize (old_length);
 	  return false;
 	}
 
+	// points might have been reallocated. Relocate phantoms.
+	phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
+
 	auto comp_points = all_points.as_array ().sub_array (old_count);
 
 	/* Copy phantom points from component if USE_MY_METRICS flag set */
@@ -455,7 +471,7 @@ struct Glyph
 	  item.get_transformation (matrix, default_trans);
 
 	  /* Apply component transformation & translation (with deltas applied) */
-	  item.transform_points (comp_points, matrix, points[comp_index]);
+	  item.transform_points (comp_points, matrix, points[old_length + comp_index]);
 	}
 
 	if (item.is_anchored () && !phantom_only)
@@ -476,12 +492,11 @@ struct Glyph
 
 	if (all_points.length > HB_GLYF_MAX_POINTS)
 	{
-	  current_glyphs->del (item_gid);
+	  points.resize (old_length);
 	  return false;
 	}
 
 	comp_index++;
-        current_glyphs->del (item_gid);
       }
 
       if (head_maxp_info && depth == 0)
@@ -492,9 +507,13 @@ struct Glyph
         head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
       }
       all_points.extend (phantoms);
+      shift = phantoms[PHANTOM_LEFT].x;
+      points.resize (old_length);
     } break;
     case EMPTY:
       all_points.extend (phantoms);
+      shift = phantoms[PHANTOM_LEFT].x;
+      points.resize (old_length);
       break;
     }
 
@@ -503,10 +522,9 @@ struct Glyph
       /* Undocumented rasterizer behavior:
        * Shift points horizontally by the updated left side bearing
        */
-      float v = -phantoms[PHANTOM_LEFT].x;
-      if (v)
+      if (shift)
         for (auto &point : all_points)
-	  point.x += v;
+	  point.x -= shift;
     }
 
     return !all_points.in_error ();
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SimpleGlyph.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SimpleGlyph.hh
index 1d42cc292..c0f01405c 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SimpleGlyph.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/SimpleGlyph.hh
@@ -127,19 +127,20 @@ struct SimpleGlyph
 			  hb_array_t<contour_point_t> points_ /* IN/OUT */,
 			  const HBUINT8 *end)
   {
+    auto *points = points_.arrayZ;
     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;
+      points[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; i++)
-	  points_.arrayZ[i].flag = flag;
+	  points[i].flag = flag;
       }
     }
     return true;
@@ -160,10 +161,7 @@ struct SimpleGlyph
       if (flag & short_flag)
       {
 	if (unlikely (p + 1 > end)) return false;
-	if (flag & same_flag)
-	  v += *p++;
-	else
-	  v -= *p++;
+	v += (bool(flag & same_flag) * 2 - 1) * *p++;
       }
       else
       {
@@ -190,7 +188,7 @@ struct SimpleGlyph
     unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
 
     unsigned old_length = points.length;
-    points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
+    points.alloc (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy
     if (unlikely (!points.resize (points.length + num_points, false))) return false;
     auto points_ = points.as_array ().sub_array (old_length);
     if (!phantom_only)
@@ -281,9 +279,9 @@ struct SimpleGlyph
     unsigned num_points = all_points.length - 4;
 
     hb_vector_t<uint8_t> flags, x_coords, y_coords;
-    if (unlikely (!flags.alloc (num_points, true))) return false;
-    if (unlikely (!x_coords.alloc (2*num_points, true))) return false;
-    if (unlikely (!y_coords.alloc (2*num_points, true))) return false;
+    if (unlikely (!flags.alloc_exact (num_points))) return false;
+    if (unlikely (!x_coords.alloc_exact (2*num_points))) return false;
+    if (unlikely (!y_coords.alloc_exact (2*num_points))) return false;
 
     unsigned lastflag = 255, repeat = 0;
     int prev_x = 0, prev_y = 0;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh
index f346ae05d..fe4ae7b8c 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/glyf.hh
@@ -94,7 +94,7 @@ struct glyf
     }
 
     hb_vector_t<unsigned> padded_offsets;
-    if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true)))
+    if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
       return_trace (false);
 
     hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
@@ -172,6 +172,9 @@ struct glyf_accelerator_t
     glyf_table = nullptr;
 #ifndef HB_NO_VAR
     gvar = nullptr;
+#ifndef HB_NO_BEYOND_64K
+    GVAR = nullptr;
+#endif
 #endif
     hmtx = nullptr;
 #ifndef HB_NO_VERTICAL
@@ -187,6 +190,9 @@ struct glyf_accelerator_t
     glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
 #ifndef HB_NO_VAR
     gvar = face->table.gvar;
+#ifndef HB_NO_BEYOND_64K
+    GVAR = face->table.GVAR;
+#endif
 #endif
     hmtx = face->table.hmtx;
 #ifndef HB_NO_VERTICAL
@@ -198,6 +204,13 @@ struct glyf_accelerator_t
   }
   ~glyf_accelerator_t ()
   {
+    auto *scratch = cached_scratch.get_relaxed ();
+    if (scratch)
+    {
+      scratch->~hb_glyf_scratch_t ();
+      hb_free (scratch);
+    }
+
     glyf_table.destroy ();
   }
 
@@ -206,21 +219,16 @@ struct glyf_accelerator_t
   protected:
   template<typename T>
   bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
-		   hb_array_t<const int> coords = hb_array_t<const int> ()) const
+		   hb_array_t<const int> coords,
+		   hb_glyf_scratch_t &scratch) const
   {
-    if (!coords)
-      coords = hb_array (font->coords, font->num_coords);
-
     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;
+    auto &all_points = scratch.all_points;
+    all_points.resize (0);
 
     bool phantom_only = !consumer.is_consuming_contour_points ();
-    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only, coords)))
+    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords)))
       return false;
 
     unsigned count = all_points.length;
@@ -229,8 +237,61 @@ struct glyf_accelerator_t
 
     if (consumer.is_consuming_contour_points ())
     {
-      for (auto &point : all_points.as_array ().sub_array (0, count))
-	consumer.consume_point (point);
+      auto *points = all_points.arrayZ;
+
+      if (false)
+      {
+	/* Our path-builder was designed to work with this simple loop.
+	 * But FreeType and CoreText do it differently, so we match those
+	 * with the other, more complicated, code branch below. */
+	for (unsigned i = 0; i < count; i++)
+	{
+	  consumer.consume_point (points[i]);
+	  if (points[i].is_end_point)
+	    consumer.contour_end ();
+	}
+      }
+      else
+      {
+	for (unsigned i = 0; i < count; i++)
+	{
+	  // Start of a contour.
+	  if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
+	  {
+	    // First point is on-curve. Draw the contour.
+	    for (; i < count; i++)
+	    {
+	      consumer.consume_point (points[i]);
+	      if (points[i].is_end_point)
+	      {
+		consumer.contour_end ();
+		break;
+	      }
+	    }
+	  }
+	  else
+	  {
+	    unsigned start = i;
+
+	    // Find end of the contour.
+	    for (; i < count; i++)
+	      if (points[i].is_end_point)
+		break;
+
+	    unsigned end = i;
+
+	    // Enough to start from the end. Our path-builder takes care of the rest.
+	    if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
+	      consumer.consume_point (points[end]);
+
+	    for (i = start; i < end; i++)
+	      consumer.consume_point (points[i]);
+
+	    consumer.contour_end ();
+	  }
+	}
+      }
+
       consumer.points_end ();
     }
 
@@ -303,6 +364,7 @@ struct glyf_accelerator_t
 
     HB_ALWAYS_INLINE
     void consume_point (const contour_point_t &point) { bounds.add (point); }
+    void contour_end () {}
     void points_end () { bounds.get_extents (font, extents, scaled); }
 
     bool is_consuming_contour_points () { return extents; }
@@ -318,7 +380,12 @@ struct glyf_accelerator_t
 
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
     if (font->num_coords)
-      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false));
+    {
+      hb_glyf_scratch_t scratch;
+      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
+			    hb_array (font->coords, font->num_coords),
+			    scratch);
+    }
 
     if (unlikely (!success))
       return
@@ -338,9 +405,11 @@ struct glyf_accelerator_t
     if (unlikely (gid >= num_glyphs)) return false;
 
     hb_glyph_extents_t extents;
-
+    hb_glyf_scratch_t scratch;
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
-    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false))))
+    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false),
+			       hb_array (font->coords, font->num_coords),
+			       scratch)))
       return false;
 
     *lsb = is_vertical
@@ -366,20 +435,16 @@ struct glyf_accelerator_t
 
 #ifndef HB_NO_VAR
     if (font->num_coords)
-      return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true));
+    {
+      hb_glyf_scratch_t scratch;
+      return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true),
+			 hb_array (font->coords, font->num_coords),
+			 scratch);
+    }
 #endif
     return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
   }
 
-  bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
-  {
-    funcs->push_clip_glyph (data, gid, font);
-    funcs->color (data, true, foreground);
-    funcs->pop_clip (data);
-
-    return true;
-  }
-
   const glyf_impl::Glyph
   glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
   {
@@ -410,15 +475,52 @@ struct glyf_accelerator_t
 
   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)); }
+  {
+    if (!has_data ()) return false;
+
+    hb_glyf_scratch_t *scratch;
+
+    // Borrow the cached strach buffer.
+    {
+      scratch = cached_scratch.get_acquire ();
+      if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+      {
+	scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
+	if (unlikely (!scratch))
+	  return true;
+      }
+    }
+
+    bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
+			   hb_array (font->coords, font->num_coords),
+			   *scratch);
+
+    // Put it back.
+    if (!cached_scratch.cmpexch (nullptr, scratch))
+    {
+      scratch->~hb_glyf_scratch_t ();
+      hb_free (scratch);
+    }
+
+    return ret;
+  }
 
   bool
   get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
-	       hb_array_t<const int> coords) const
-  { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session), coords); }
+	       hb_array_t<const int> coords,
+	       hb_glyf_scratch_t &scratch) const
+  {
+    if (!has_data ()) return false;
+    return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
+		       coords,
+		       scratch);
+  }
 
 #ifndef HB_NO_VAR
   const gvar_accelerator_t *gvar;
+#ifndef HB_NO_BEYOND_64K
+  const GVAR_accelerator_t *GVAR;
+#endif
 #endif
   const hmtx_accelerator_t *hmtx;
 #ifndef HB_NO_VERTICAL
@@ -430,6 +532,7 @@ struct glyf_accelerator_t
   unsigned int num_glyphs;
   hb_blob_ptr_t<loca> loca_table;
   hb_blob_ptr_t<glyf> glyf_table;
+  hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
 };
 
 
@@ -439,7 +542,7 @@ 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);
-  if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false;
+  if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
 
   for (const auto &pair : plan->new_to_old_gid_list)
   {
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
index f55052450..859cd577f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/path-builder.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/glyf/path-builder.hh
@@ -42,7 +42,7 @@ struct path_builder_t
   {
     bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
 #ifdef HB_NO_CUBIC_GLYF
-    bool is_cubic = false;
+    constexpr bool is_cubic = false;
 #else
     bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
 #endif
@@ -124,58 +124,60 @@ struct path_builder_t
       }
     }
 
-    if (unlikely (point.is_end_point))
-    {
-      if (first_offcurve && last_offcurve)
-      {
-	optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
-						  first_offcurve2 :
-						  first_offcurve);
-	if (last_offcurve2)
-	  draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
-				  last_offcurve.x, last_offcurve.y,
-				  mid.x, mid.y);
-	else
-	  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)
-      {
-        if (first_offcurve2)
-	  draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
-				  first_offcurve.x, first_offcurve.y,
-				  first_oncurve.x, first_oncurve.y);
-	else
-	  draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
-				     first_oncurve.x, first_oncurve.y);
-      }
-      else if (last_offcurve && first_oncurve)
-      {
-	if (last_offcurve2)
-	  draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
-				  last_offcurve.x, last_offcurve.y,
-				  first_oncurve.x, first_oncurve.y);
-	else
-	  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);
-      }
+  void contour_end ()
+  {
+    if (first_offcurve && last_offcurve)
+    {
+      optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
+						first_offcurve2 :
+						first_offcurve);
+      if (last_offcurve2)
+	draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				last_offcurve.x, last_offcurve.y,
+				mid.x, mid.y);
+      else
+	draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				   mid.x, mid.y);
+      last_offcurve = optional_point_t ();
+    }
+    /* now check the rest */
 
-      /* Getting ready for the next contour */
-      first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
-      draw_session->close_path ();
+    if (first_offcurve && first_oncurve)
+    {
+      if (first_offcurve2)
+	draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
+				first_offcurve.x, first_offcurve.y,
+				first_oncurve.x, first_oncurve.y);
+      else
+	draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
+				   first_oncurve.x, first_oncurve.y);
     }
+    else if (last_offcurve && first_oncurve)
+    {
+      if (last_offcurve2)
+	draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				last_offcurve.x, last_offcurve.y,
+				first_oncurve.x, first_oncurve.y);
+      else
+	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 = last_offcurve2 = optional_point_t ();
+    draw_session->close_path ();
   }
+
   void points_end () {}
 
   bool is_consuming_contour_points () { return true; }
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/name/name.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/name/name.hh
index e2a25d4a0..33de82d35 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/OT/name/name.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/name/name.hh
@@ -163,7 +163,7 @@ struct NameRecord
       if (platformID != 1)
       {
         unsigned text_size = hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, nullptr, nullptr);
-  
+
         text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf()
         unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
         name_str_utf16_be = (char *) hb_calloc (byte_len, 1);
@@ -174,14 +174,14 @@ struct NameRecord
         }
         hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, &text_size,
                                                           (hb_utf16_be_t::codepoint_t *) name_str_utf16_be);
-  
+
         unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
         if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
           c->revert (snap);
           hb_free (name_str_utf16_be);
           return_trace (nullptr);
         }
-  
+
         encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len);
       }
       else
@@ -392,7 +392,7 @@ struct name
     const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
         &c->plan->name_table_overrides;
 #endif
-    
+
     auto it =
     + nameRecordZ.as_array (count)
     | hb_filter (c->plan->name_ids, &NameRecord::nameID)
@@ -485,7 +485,7 @@ struct name
       const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
 						    this->table->count);
 
-      this->names.alloc (all_names.length, true);
+      this->names.alloc_exact (all_names.length);
 
       for (unsigned int i = 0; i < all_names.length; i++)
       {
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 3c1f6211e..adb0cc043 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/gen-vowel-constraints.py
+++ b/source/libs/harfbuzz/harfbuzz-src/src/gen-vowel-constraints.py
@@ -172,7 +172,7 @@ print ('static void')
 print ('_output_dotted_circle (hb_buffer_t *buffer)')
 print ('{')
 print ('  (void) buffer->output_glyph (0x25CCu);')
-print ('  _hb_glyph_info_reset_continuation (&buffer->prev());')
+print ('  _hb_glyph_info_clear_continuation (&buffer->prev());')
 print ('}')
 print ()
 print ('static void')
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-common.hh
index 2ea86a2a1..d2626b45f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-common.hh
@@ -30,6 +30,10 @@
 #include "hb-aat-layout.hh"
 #include "hb-aat-map.hh"
 #include "hb-open-type.hh"
+#include "hb-cache.hh"
+#include "hb-bit-set.hh"
+#include "hb-bit-page.hh"
+
 
 namespace OT {
 struct GDEF;
@@ -39,10 +43,11 @@ namespace AAT {
 
 using namespace OT;
 
-#define HB_AAT_BUFFER_DIGEST_THRESHOLD 32
-
 struct ankr;
 
+using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>;
+static_assert (sizeof (hb_aat_class_cache_t) == 256, "");
+
 struct hb_aat_apply_context_t :
        hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
 {
@@ -61,10 +66,12 @@ struct hb_aat_apply_context_t :
   const ankr *ankr_table;
   const OT::GDEF *gdef_table;
   const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
-  hb_set_digest_t buffer_digest = hb_set_digest_t::full ();
-  hb_set_digest_t machine_glyph_set = hb_set_digest_t::full ();
-  hb_set_digest_t left_set = hb_set_digest_t::full ();
-  hb_set_digest_t right_set = hb_set_digest_t::full ();
+  bool using_buffer_glyph_set = false;
+  hb_bit_set_t buffer_glyph_set;
+  const hb_bit_set_t *left_set = nullptr;
+  const hb_bit_set_t *right_set = nullptr;
+  const hb_bit_set_t *machine_glyph_set = nullptr;
+  hb_aat_class_cache_t *machine_class_cache = nullptr;
   hb_mask_t subtable_flags = 0;
 
   /* Unused. For debug tracing only. */
@@ -80,6 +87,25 @@ struct hb_aat_apply_context_t :
   HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
 
   void set_lookup_index (unsigned int i) { lookup_index = i; }
+
+  void setup_buffer_glyph_set ()
+  {
+    using_buffer_glyph_set = buffer->len >= 4;
+
+    if (using_buffer_glyph_set)
+      buffer->collect_codepoints (buffer_glyph_set);
+  }
+  bool buffer_intersects_machine () const
+  {
+    if (using_buffer_glyph_set)
+      return buffer_glyph_set.intersects (*machine_glyph_set);
+
+    // Faster for shorter buffers.
+    for (unsigned i = 0; i < buffer->len; i++)
+      if (machine_glyph_set->has (buffer->info[i].codepoint))
+	return true;
+    return false;
+  }
 };
 
 
@@ -108,6 +134,13 @@ struct LookupFormat0
   {
     glyphs.add_range (0, num_glyphs - 1);
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    for (unsigned i = 0; i < num_glyphs; i++)
+      if (filter (arrayZ[i]))
+	glyphs.add (i);
+  }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -140,8 +173,13 @@ struct LookupSegmentSingle
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   {
-    if (first == DELETED_GLYPH)
-      return;
+    if (first == DELETED_GLYPH) return;
+    glyphs.add_range (first, last);
+  }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (!filter (value)) return;
     glyphs.add_range (first, last);
   }
 
@@ -182,6 +220,13 @@ struct LookupFormat2
     for (unsigned int i = 0; i < count; i++)
       segments[i].collect_glyphs (glyphs);
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned int i = 0; i < count; i++)
+      segments[i].collect_glyphs_filtered (glyphs, filter);
+  }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -217,10 +262,17 @@ struct LookupSegmentArray
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   {
-    if (first == DELETED_GLYPH)
-      return;
+    if (first == DELETED_GLYPH) return;
     glyphs.add_range (first, last);
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const void *base, const filter_t &filter) const
+  {
+    const auto &values = base+valuesZ;
+    for (hb_codepoint_t i = first; i <= last; i++)
+      if (filter (values[i - first]))
+	glyphs.add (i);
+  }
 
   int cmp (hb_codepoint_t g) const
   { return g < first ? -1 : g <= last ? 0 : +1; }
@@ -271,6 +323,13 @@ struct LookupFormat4
     for (unsigned i = 0; i < count; i++)
       segments[i].collect_glyphs (glyphs);
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      segments[i].collect_glyphs_filtered (glyphs, this, filter);
+  }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -303,8 +362,13 @@ struct LookupSingle
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   {
-    if (glyph == DELETED_GLYPH)
-      return;
+    if (glyph == DELETED_GLYPH) return;
+    glyphs.add (glyph);
+  }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (!filter (value)) return;
     glyphs.add (glyph);
   }
 
@@ -344,6 +408,13 @@ struct LookupFormat6
     for (unsigned i = 0; i < count; i++)
       entries[i].collect_glyphs (glyphs);
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = entries.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      entries[i].collect_glyphs_filtered (glyphs, filter);
+  }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -379,12 +450,20 @@ struct LookupFormat8
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   {
-    if (unlikely (!glyphCount))
-      return;
-    if (firstGlyph == DELETED_GLYPH)
-      return;
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
     glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
+    const T *p = valueArrayZ.arrayZ;
+    for (unsigned i = 0; i < glyphCount; i++)
+      if (filter (p[i]))
+	glyphs.add (firstGlyph + i);
+  }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -433,10 +512,8 @@ struct LookupFormat10
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   {
-    if (unlikely (!glyphCount))
-      return;
-    if (firstGlyph == DELETED_GLYPH)
-      return;
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
     glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
   }
 
@@ -501,6 +578,18 @@ struct Lookup
     default:return;
     }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    switch (u.format) {
+    case 0: hb_barrier (); u.format0.collect_glyphs_filtered (glyphs, num_glyphs, filter); return;
+    case 2: hb_barrier (); u.format2.collect_glyphs_filtered (glyphs, filter); return;
+    case 4: hb_barrier (); u.format4.collect_glyphs_filtered (glyphs, filter); return;
+    case 6: hb_barrier (); u.format6.collect_glyphs_filtered (glyphs, filter); return;
+    case 8: hb_barrier (); u.format8.collect_glyphs_filtered (glyphs, filter); return;
+    default:return;
+    }
+  }
 
   typename T::type get_class (hb_codepoint_t glyph_id,
 			      unsigned int num_glyphs,
@@ -563,7 +652,7 @@ DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
 template <typename T>
 struct Entry
 {
-  // This does seem like it's ever called.
+  // This doesn't seem like it's ever called.
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -632,18 +721,47 @@ struct StateTable
   {
     (this+classTable).collect_glyphs (glyphs, num_glyphs);
   }
+  template <typename set_t, typename table_t>
+  void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs, const table_t &table) const
+  {
+    unsigned num_classes = nClasses;
+
+    if (unlikely (num_classes > hb_bit_page_t::BITS))
+    {
+      (this+classTable).collect_glyphs (glyphs, num_glyphs);
+      return;
+    }
+
+    // Collect all classes going out from the start state.
+    hb_bit_page_t filter;
+
+    for (unsigned i = 0; i < num_classes; i++)
+    {
+      const auto &entry = get_entry (STATE_START_OF_TEXT, i);
+      if (new_state (entry.newState) == STATE_START_OF_TEXT &&
+	  !table.is_action_initiable (entry) && !table.is_actionable (entry))
+	continue;
+
+      filter.add (i);
+    }
+
+    // And glyphs in those classes.
+    (this+classTable).collect_glyphs_filtered (glyphs, num_glyphs, filter);
+  }
 
   int new_state (unsigned int newState) const
   { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; }
 
-  template <typename set_t>
   unsigned int get_class (hb_codepoint_t glyph_id,
 			  unsigned int num_glyphs,
-			  const set_t &glyphs) const
+			  hb_aat_class_cache_t *cache = nullptr) const
   {
+    unsigned klass;
+    if (cache && cache->get (glyph_id, &klass)) return klass;
     if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
-    if (!glyphs[glyph_id]) return CLASS_OUT_OF_BOUNDS;
-    return (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
+    klass = (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
+    if (cache) cache->set (glyph_id, klass);
+    return klass;
   }
 
   const Entry<Extra> *get_entries () const
@@ -651,13 +769,14 @@ struct StateTable
 
   const Entry<Extra> &get_entry (int state, unsigned int klass) const
   {
-    if (unlikely (klass >= nClasses))
+    unsigned n_classes = nClasses;
+    if (unlikely (klass >= n_classes))
       klass = CLASS_OUT_OF_BOUNDS;
 
     const HBUSHORT *states = (this+stateArrayTable).arrayZ;
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
-    unsigned int entry = states[state * nClasses + klass];
+    unsigned int entry = states[state * n_classes + klass];
     DEBUG_MSG (APPLY, nullptr, "e%u", entry);
 
     return entries[entry];
@@ -803,6 +922,13 @@ struct ClassTable
       if (classArray.arrayZ[i] != CLASS_OUT_OF_BOUNDS)
 	glyphs.add (firstGlyph + i);
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    for (unsigned i = 0; i < classArray.len; i++)
+      if (filter (classArray.arrayZ[i]))
+	glyphs.add (firstGlyph + i);
+  }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -918,7 +1044,7 @@ struct ExtendedTypes
   }
 };
 
-template <typename Types, typename EntryData>
+template <typename Types, typename EntryData, typename Flags>
 struct StateTableDriver
 {
   using StateTableT = StateTable<Types, EntryData>;
@@ -929,14 +1055,6 @@ struct StateTableDriver
 	      machine (machine_),
 	      num_glyphs (face_->get_num_glyphs ()) {}
 
-  template <typename context_t>
-  bool is_idempotent_on_all_out_of_bounds (context_t *c, hb_aat_apply_context_t *ac)
-  {
-    const auto entry = machine.get_entry (StateTableT::STATE_START_OF_TEXT, CLASS_OUT_OF_BOUNDS);
-    return !c->is_actionable (ac->buffer, this, entry) &&
-	    machine.new_state (entry.newState) == StateTableT::STATE_START_OF_TEXT;
-  }
-
   template <typename context_t>
   void drive (context_t *c, hb_aat_apply_context_t *ac)
   {
@@ -977,7 +1095,7 @@ struct StateTableDriver
       }
 
       unsigned int klass = likely (buffer->idx < buffer->len) ?
-			   machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_glyph_set) :
+			   machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) :
 			   (unsigned) CLASS_END_OF_TEXT;
       DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       const EntryT &entry = machine.get_entry (state, klass);
@@ -1011,41 +1129,36 @@ struct StateTableDriver
        *
        *   https://github.com/harfbuzz/harfbuzz/issues/2860
        */
-
-      const auto is_safe_to_break_extra = [&]()
-      {
-          /* 2c. */
-          const auto &wouldbe_entry = machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass);
-
-          /* 2c'. */
-          if (c->is_actionable (buffer, this, wouldbe_entry))
-	    return false;
-
-          /* 2c". */
-          return next_state == machine.new_state(wouldbe_entry.newState)
-              && (entry.flags & context_t::DontAdvance) == (wouldbe_entry.flags & context_t::DontAdvance);
-      };
-
-      const auto is_safe_to_break = [&]()
-      {
+      const EntryT *wouldbe_entry;
+      bool is_safe_to_break =
+      (
           /* 1. */
-          if (c->is_actionable (buffer, this, entry))
-              return false;
+          !c->table->is_actionable (entry) &&
 
           /* 2. */
           // This one is meh, I know...
-          const auto ok =
+	  (
                  state == StateTableT::STATE_START_OF_TEXT
-              || ((entry.flags & context_t::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
-              || is_safe_to_break_extra();
-          if (!ok)
-              return false;
+              || ((entry.flags & Flags::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
+              || (
+		    /* 2c. */
+		    wouldbe_entry = &machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass)
+		    ,
+		    /* 2c'. */
+		    !c->table->is_actionable (*wouldbe_entry) &&
+		    /* 2c". */
+		    (
+		      next_state == machine.new_state(wouldbe_entry->newState) &&
+		      (entry.flags & Flags::DontAdvance) == (wouldbe_entry->flags & Flags::DontAdvance)
+		    )
+		 )
+	  ) &&
 
           /* 3. */
-          return !c->is_actionable (buffer, this, machine.get_entry (state, CLASS_END_OF_TEXT));
-      };
+          !c->table->is_actionable (machine.get_entry (state, CLASS_END_OF_TEXT))
+      );
 
-      if (!is_safe_to_break () && buffer->backtrack_len () && buffer->idx < buffer->len)
+      if (!is_safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len)
 	buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
 
       c->transition (buffer, this, entry);
@@ -1056,7 +1169,7 @@ struct StateTableDriver
       if (buffer->idx == buffer->len || unlikely (!buffer->successful))
 	break;
 
-      if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0)
+      if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0)
 	(void) buffer->next_glyph ();
     }
 
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 c01c31d73..4b980ca89 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
@@ -112,10 +112,6 @@ struct KerxSubTableFormat0
     if (header.coverage & header.Backwards)
       return_trace (false);
 
-    if (!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
-
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -144,7 +140,7 @@ struct KerxSubTableFormat0
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     {
-      if (!c->left_set[left] || !c->right_set[right]) return 0;
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
       return table.get_kerning (left, right, c);
     }
   };
@@ -211,6 +207,9 @@ struct Format1Entry<false>
 
   typedef void EntryData;
 
+  static bool initiateAction (const Entry<EntryData> &entry)
+  { return entry.flags & Push; }
+
   static bool performAction (const Entry<EntryData> &entry)
   { return entry.flags & Offset; }
 
@@ -227,13 +226,23 @@ struct KerxSubTableFormat1
   typedef Format1Entry<Types::extended> Format1EntryT;
   typedef typename Format1EntryT::EntryData EntryData;
 
+  enum Flags
+  {
+    DontAdvance	= Format1EntryT::DontAdvance,
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return Format1EntryT::initiateAction (entry);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return Format1EntryT::performAction (entry);
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = true;
-    enum
-    {
-      DontAdvance	= Format1EntryT::DontAdvance,
-    };
 
     driver_context_t (const KerxSubTableFormat1 *table_,
 		      hb_aat_apply_context_t *c_) :
@@ -246,12 +255,8 @@ struct KerxSubTableFormat1
 	depth (0),
 	crossStream (table->header.coverage & table->header.CrossStream) {}
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry)
-    { return Format1EntryT::performAction (entry); }
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
     {
       unsigned int flags = entry.flags;
@@ -351,9 +356,10 @@ struct KerxSubTableFormat1
       }
     }
 
-    private:
+    public:
     hb_aat_apply_context_t *c;
     const KerxSubTableFormat1 *table;
+    private:
     const UnsizedArrayOf<FWORD> &kernAction;
     unsigned int stack[8];
     unsigned int depth;
@@ -370,12 +376,7 @@ struct KerxSubTableFormat1
 
     driver_context_t dc (this, c);
 
-    StateTableDriver<Types, EntryData> driver (machine, c->font->face);
-
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
 
     driver.drive (&dc, c);
 
@@ -440,10 +441,6 @@ struct KerxSubTableFormat2
     if (header.coverage & header.Backwards)
       return_trace (false);
 
-    if (!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
-
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -469,7 +466,7 @@ struct KerxSubTableFormat2
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     {
-      if (!c->left_set[left] || !c->right_set[right]) return 0;
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
       return table.get_kerning (left, right, c);
     }
   };
@@ -513,17 +510,26 @@ struct KerxSubTableFormat4
     DEFINE_SIZE_STATIC (2);
   };
 
-  struct driver_context_t
+  enum Flags
   {
-    static constexpr bool in_place = true;
-    enum Flags
-    {
-      Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
+    Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph before
 					 * going to the new state. */
-      Reserved		= 0x3FFF,	/* Not used; set to 0. */
-    };
+    Reserved		= 0x3FFF,	/* Not used; set to 0. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & Mark);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return entry.data.ankrActionIndex != 0xFFFF;
+  }
 
+  struct driver_context_t
+  {
+    static constexpr bool in_place = true;
     enum SubTableFlags
     {
       ActionType	= 0xC0000000,	/* A two-bit field containing the action type. */
@@ -533,20 +539,17 @@ struct KerxSubTableFormat4
 					 * point table. */
     };
 
-    driver_context_t (const KerxSubTableFormat4 *table,
+    driver_context_t (const KerxSubTableFormat4 *table_,
 		      hb_aat_apply_context_t *c_) :
 	c (c_),
+	table (table_),
 	action_type ((table->flags & ActionType) >> 30),
 	ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
 	mark_set (false),
 	mark (0) {}
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry)
-    { return entry.data.ankrActionIndex != 0xFFFF; }
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
     {
       if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
@@ -634,8 +637,10 @@ struct KerxSubTableFormat4
       }
     }
 
-    private:
+    public:
     hb_aat_apply_context_t *c;
+    const KerxSubTableFormat4 *table;
+    private:
     unsigned int action_type;
     const HBUINT16 *ankrData;
     bool mark_set;
@@ -648,12 +653,7 @@ struct KerxSubTableFormat4
 
     driver_context_t dc (this, c);
 
-    StateTableDriver<Types, EntryData> driver (machine, c->font->face);
-
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
 
     driver.drive (&dc, c);
 
@@ -735,10 +735,6 @@ struct KerxSubTableFormat6
     if (header.coverage & header.Backwards)
       return_trace (false);
 
-    if (!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
-
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -793,7 +789,7 @@ struct KerxSubTableFormat6
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     {
-      if (!c->left_set[left] || !c->right_set[right]) return 0;
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
       return table.get_kerning (left, right, c);
     }
   };
@@ -925,7 +921,7 @@ struct KerxSubTable
  * The 'kerx' Table
  */
 
-using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_set_digest_t, hb_set_digest_t>>;
+using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_bit_set_t, hb_bit_set_t>>;
 
 template <typename T>
 struct KerxTable
@@ -985,15 +981,10 @@ struct KerxTable
   }
 
   bool apply (AAT::hb_aat_apply_context_t *c,
-	      const kern_accelerator_data_t *accel_data = nullptr) const
+	      const kern_accelerator_data_t &accel_data) const
   {
     c->buffer->unsafe_to_concat ();
 
-    if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD)
-      c->buffer_digest = c->buffer->digest ();
-    else
-      c->buffer_digest = hb_set_digest_t::full ();
-
     typedef typename T::SubTable SubTable;
 
     bool ret = false;
@@ -1037,15 +1028,8 @@ struct KerxTable
       if (reverse)
 	c->buffer->reverse ();
 
-      if (accel_data)
-      {
-	c->left_set = (*accel_data)[i].first;
-	c->right_set = (*accel_data)[i].second;
-      }
-      else
-      {
-        c->left_set = c->right_set = hb_set_digest_t::full ();
-      }
+      c->left_set = &accel_data[i].first;
+      c->right_set = &accel_data[i].second;
 
       {
 	/* See comment in sanitize() for conditional here. */
@@ -1122,7 +1106,7 @@ struct KerxTable
     unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      hb_set_digest_t left_set, right_set;
+      hb_bit_set_t left_set, right_set;
       st->collect_glyphs (left_set, right_set, num_glyphs);
       accel_data.push (hb_pair (left_set, right_set));
       st = &StructAfter<SubTable> (*st);
@@ -1148,7 +1132,7 @@ struct KerxTable
 
     bool apply (AAT::hb_aat_apply_context_t *c) const
     {
-      return table->apply (c, &accel_data);
+      return table->apply (c, accel_data);
     }
 
     hb_blob_ptr_t<T> table;
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 d31834402..617a239fe 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
@@ -29,6 +29,7 @@
 
 #include "hb-open-type.hh"
 #include "hb-aat-layout-common.hh"
+#include "hb-ot-layout.hh"
 #include "hb-ot-layout-common.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-aat-map.hh"
@@ -53,35 +54,40 @@ struct RearrangementSubtable
 
   typedef void EntryData;
 
-  struct driver_context_t
+  enum Flags
   {
-    static constexpr bool in_place = true;
-    enum Flags
-    {
-      MarkFirst		= 0x8000,	/* If set, make the current glyph the first
+    MarkFirst		= 0x8000,	/* If set, make the current glyph the first
 					 * glyph to be rearranged. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
 					 * before going to the new state. This means
 					 * that the glyph index doesn't change, even
 					 * if the glyph at that index has changed. */
-      MarkLast		= 0x2000,	/* If set, make the current glyph the last
+    MarkLast		= 0x2000,	/* If set, make the current glyph the last
 					 * glyph to be rearranged. */
-      Reserved		= 0x1FF0,	/* These bits are reserved and should be set to 0. */
-      Verb		= 0x000F,	/* The type of rearrangement specified. */
-    };
+    Reserved		= 0x1FF0,	/* These bits are reserved and should be set to 0. */
+    Verb		= 0x000F,	/* The type of rearrangement specified. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & MarkFirst);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & Verb);
+  }
+
+  struct driver_context_t
+  {
+    static constexpr bool in_place = true;
 
-    driver_context_t (const RearrangementSubtable *table HB_UNUSED) :
+    driver_context_t (const RearrangementSubtable *table_) :
 	ret (false),
+	table (table_),
 	start (0), end (0) {}
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry) const
-    {
-      return (entry.flags & Verb) && start < end;
-    }
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
     {
       unsigned int flags = entry.flags;
@@ -158,6 +164,7 @@ struct RearrangementSubtable
 
     public:
     bool ret;
+    const RearrangementSubtable *table;
     private:
     unsigned int start;
     unsigned int end;
@@ -169,11 +176,13 @@ struct RearrangementSubtable
 
     driver_context_t dc (this);
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
+    }
 
     driver.drive (&dc, c);
 
@@ -207,39 +216,40 @@ struct ContextualSubtable
     DEFINE_SIZE_STATIC (4);
   };
 
+  enum Flags
+  {
+    SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph before
+					 * going to the new state. */
+    Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & SetMark);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = true;
-    enum Flags
-    {
-      SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
-					 * going to the new state. */
-      Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
-    };
 
     driver_context_t (const ContextualSubtable *table_,
 			     hb_aat_apply_context_t *c_) :
 	ret (false),
 	c (c_),
+	table (table_),
 	gdef (*c->gdef_table),
 	mark_set (false),
 	has_glyph_classes (gdef.has_glyph_classes ()),
 	mark (0),
-	table (table_),
 	subs (table+table->substitutionTables) {}
 
-    bool is_actionable (hb_buffer_t *buffer,
-			StateTableDriver<Types, EntryData> *driver,
-			const Entry<EntryData> &entry) const
-    {
-      if (buffer->idx == buffer->len && !mark_set)
-	return false;
-
-      return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
-    }
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
     {
       /* Looks like CoreText applies neither mark nor current substitution for
@@ -271,8 +281,9 @@ struct ContextualSubtable
       if (replacement)
       {
 	buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
-	buffer->info[mark].codepoint = *replacement;
-	c->buffer_digest.add (*replacement);
+	hb_codepoint_t glyph = *replacement;
+	buffer->info[mark].codepoint = glyph;
+	c->buffer_glyph_set.add (glyph);
 	if (has_glyph_classes)
 	  _hb_glyph_info_set_glyph_props (&buffer->info[mark],
 					  gdef.get_glyph_props (*replacement));
@@ -301,8 +312,9 @@ struct ContextualSubtable
       }
       if (replacement)
       {
-	buffer->info[idx].codepoint = *replacement;
-	c->buffer_digest.add (*replacement);
+	hb_codepoint_t glyph = *replacement;
+	buffer->info[idx].codepoint = glyph;
+	c->buffer_glyph_set.add (glyph);
 	if (has_glyph_classes)
 	  _hb_glyph_info_set_glyph_props (&buffer->info[idx],
 					  gdef.get_glyph_props (*replacement));
@@ -318,13 +330,13 @@ struct ContextualSubtable
 
     public:
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
+    const ContextualSubtable *table;
+    private:
     const OT::GDEF &gdef;
     bool mark_set;
     bool has_glyph_classes;
     unsigned int mark;
-    const ContextualSubtable *table;
     const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs;
   };
 
@@ -334,11 +346,13 @@ struct ContextualSubtable
 
     driver_context_t dc (this, c);
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
+    }
 
     driver.drive (&dc, c);
 
@@ -389,6 +403,16 @@ struct LigatureEntry;
 template <>
 struct LigatureEntry<true>
 {
+
+  struct EntryData
+  {
+    HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
+				 * for processing this group, if indicated
+				 * by the flags. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
   enum Flags
   {
     SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
@@ -400,14 +424,8 @@ struct LigatureEntry<true>
     Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
   };
 
-  struct EntryData
-  {
-    HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
-				 * for processing this group, if indicated
-				 * by the flags. */
-    public:
-    DEFINE_SIZE_STATIC (2);
-  };
+  static bool initiateAction (const Entry<EntryData> &entry)
+  { return entry.flags & SetComponent; }
 
   static bool performAction (const Entry<EntryData> &entry)
   { return entry.flags & PerformAction; }
@@ -418,6 +436,8 @@ struct LigatureEntry<true>
 template <>
 struct LigatureEntry<false>
 {
+  typedef void EntryData;
+
   enum Flags
   {
     SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
@@ -429,7 +449,8 @@ struct LigatureEntry<false>
 					 * multiple of 4. */
   };
 
-  typedef void EntryData;
+  static bool initiateAction (const Entry<EntryData> &entry)
+  { return entry.flags & SetComponent; }
 
   static bool performAction (const Entry<EntryData> &entry)
   { return entry.flags & Offset; }
@@ -447,13 +468,23 @@ struct LigatureSubtable
   typedef LigatureEntry<Types::extended> LigatureEntryT;
   typedef typename LigatureEntryT::EntryData EntryData;
 
+  enum Flags
+  {
+    DontAdvance	= LigatureEntryT::DontAdvance,
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return LigatureEntryT::initiateAction (entry);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return LigatureEntryT::performAction (entry);
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = false;
-    enum
-    {
-      DontAdvance	= LigatureEntryT::DontAdvance,
-    };
     enum LigActionFlags
     {
       LigActionLast	= 0x80000000,	/* This is the last action in the list. This also
@@ -476,14 +507,8 @@ struct LigatureSubtable
 	ligature (table+table->ligature),
 	match_length (0) {}
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry) const
-    {
-      return LigatureEntryT::performAction (entry);
-    }
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
     {
       DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
@@ -564,7 +589,7 @@ struct LigatureSubtable
 	    {
 	      DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
 	      if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
-	      buffer->cur().unicode_props() |= UPROPS_MASK_IGNORABLE;
+	      _hb_glyph_info_set_default_ignorable (&buffer->cur());
 	      if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
 	    }
 
@@ -581,9 +606,9 @@ struct LigatureSubtable
 
     public:
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
     const LigatureSubtable *table;
+    private:
     const UnsizedArrayOf<HBUINT32> &ligAction;
     const UnsizedArrayOf<HBUINT16> &component;
     const UnsizedArrayOf<HBGlyphID16> &ligature;
@@ -597,11 +622,13 @@ struct LigatureSubtable
 
     driver_context_t dc (this, c);
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
+    }
 
     driver.drive (&dc, c);
 
@@ -638,6 +665,12 @@ struct NoncontextualSubtable
   {
     TRACE_APPLY (this);
 
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
+      return_trace (false);
+    }
+
     const OT::GDEF &gdef (*c->gdef_table);
     bool has_glyph_classes = gdef.has_glyph_classes ();
 
@@ -670,8 +703,9 @@ struct NoncontextualSubtable
       const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       if (replacement)
       {
-	info[i].codepoint = *replacement;
-	c->buffer_digest.add (*replacement);
+	hb_codepoint_t glyph = *replacement;
+	info[i].codepoint = glyph;
+	c->buffer_glyph_set.add (glyph);
 	if (has_glyph_classes)
 	  _hb_glyph_info_set_glyph_props (&info[i],
 					  gdef.get_glyph_props (*replacement));
@@ -682,6 +716,12 @@ struct NoncontextualSubtable
     return_trace (ret);
   }
 
+  template <typename set_t>
+  void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs) const
+  {
+    substitute.collect_glyphs (glyphs, num_glyphs);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -715,73 +755,78 @@ struct InsertionSubtable
     DEFINE_SIZE_STATIC (4);
   };
 
+  enum Flags
+  {
+    SetMark		= 0x8000,     /* If set, mark the current glyph. */
+    DontAdvance		= 0x4000,     /* If set, don't advance to the next glyph before
+				       * going to the new state.  This does not mean
+				       * that the glyph pointed to is the same one as
+				       * before. If you've made insertions immediately
+				       * downstream of the current glyph, the next glyph
+				       * processed would in fact be the first one
+				       * inserted. */
+    CurrentIsKashidaLike= 0x2000,     /* If set, and the currentInsertList is nonzero,
+				       * then the specified glyph list will be inserted
+				       * as a kashida-like insertion, either before or
+				       * after the current glyph (depending on the state
+				       * of the currentInsertBefore flag). If clear, and
+				       * the currentInsertList is nonzero, then the
+				       * specified glyph list will be inserted as a
+				       * split-vowel-like insertion, either before or
+				       * after the current glyph (depending on the state
+				       * of the currentInsertBefore flag). */
+    MarkedIsKashidaLike= 0x1000,      /* If set, and the markedInsertList is nonzero,
+				       * then the specified glyph list will be inserted
+				       * as a kashida-like insertion, either before or
+				       * after the marked glyph (depending on the state
+				       * of the markedInsertBefore flag). If clear, and
+				       * the markedInsertList is nonzero, then the
+				       * specified glyph list will be inserted as a
+				       * split-vowel-like insertion, either before or
+				       * after the marked glyph (depending on the state
+				       * of the markedInsertBefore flag). */
+    CurrentInsertBefore= 0x0800,      /* If set, specifies that insertions are to be made
+				       * to the left of the current glyph. If clear,
+				       * they're made to the right of the current glyph. */
+    MarkedInsertBefore= 0x0400,	      /* If set, specifies that insertions are to be
+				       * made to the left of the marked glyph. If clear,
+				       * they're made to the right of the marked glyph. */
+    CurrentInsertCount= 0x3E0,	      /* This 5-bit field is treated as a count of the
+				       * number of glyphs to insert at the current
+				       * position. Since zero means no insertions, the
+				       * largest number of insertions at any given
+				       * current location is 31 glyphs. */
+    MarkedInsertCount= 0x001F,	      /* This 5-bit field is treated as a count of the
+				       * number of glyphs to insert at the marked
+				       * position. Since zero means no insertions, the
+				       * largest number of insertions at any given
+				       * marked location is 31 glyphs. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & SetMark);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
+	   (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = false;
-    enum Flags
-    {
-      SetMark		= 0x8000,	/* If set, mark the current glyph. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
-					 * going to the new state.  This does not mean
-					 * that the glyph pointed to is the same one as
-					 * before. If you've made insertions immediately
-					 * downstream of the current glyph, the next glyph
-					 * processed would in fact be the first one
-					 * inserted. */
-      CurrentIsKashidaLike= 0x2000,	/* If set, and the currentInsertList is nonzero,
-					 * then the specified glyph list will be inserted
-					 * as a kashida-like insertion, either before or
-					 * after the current glyph (depending on the state
-					 * of the currentInsertBefore flag). If clear, and
-					 * the currentInsertList is nonzero, then the
-					 * specified glyph list will be inserted as a
-					 * split-vowel-like insertion, either before or
-					 * after the current glyph (depending on the state
-					 * of the currentInsertBefore flag). */
-      MarkedIsKashidaLike= 0x1000,	/* If set, and the markedInsertList is nonzero,
-					 * then the specified glyph list will be inserted
-					 * as a kashida-like insertion, either before or
-					 * after the marked glyph (depending on the state
-					 * of the markedInsertBefore flag). If clear, and
-					 * the markedInsertList is nonzero, then the
-					 * specified glyph list will be inserted as a
-					 * split-vowel-like insertion, either before or
-					 * after the marked glyph (depending on the state
-					 * of the markedInsertBefore flag). */
-      CurrentInsertBefore= 0x0800,	/* If set, specifies that insertions are to be made
-					 * to the left of the current glyph. If clear,
-					 * they're made to the right of the current glyph. */
-      MarkedInsertBefore= 0x0400,	/* If set, specifies that insertions are to be
-					 * made to the left of the marked glyph. If clear,
-					 * they're made to the right of the marked glyph. */
-      CurrentInsertCount= 0x3E0,	/* This 5-bit field is treated as a count of the
-					 * number of glyphs to insert at the current
-					 * position. Since zero means no insertions, the
-					 * largest number of insertions at any given
-					 * current location is 31 glyphs. */
-      MarkedInsertCount= 0x001F,	/* This 5-bit field is treated as a count of the
-					 * number of glyphs to insert at the marked
-					 * position. Since zero means no insertions, the
-					 * largest number of insertions at any given
-					 * marked location is 31 glyphs. */
-    };
 
-    driver_context_t (const InsertionSubtable *table,
+    driver_context_t (const InsertionSubtable *table_,
 		      hb_aat_apply_context_t *c_) :
 	ret (false),
 	c (c_),
+	table (table_),
 	mark (0),
 	insertionAction (table+table->insertionAction) {}
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry) const
-    {
-      return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
-	     (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
-    }
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
     {
       unsigned int flags = entry.flags;
@@ -807,7 +852,7 @@ struct InsertionSubtable
 	/* TODO We ignore KashidaLike setting. */
 	if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
 	for (unsigned int i = 0; i < count; i++)
-	  c->buffer_digest.add (glyphs[i]);
+	  c->buffer_glyph_set.add (glyphs[i]);
 	ret = true;
 	if (buffer->idx < buffer->len && !before)
 	  buffer->skip_glyph ();
@@ -861,8 +906,9 @@ struct InsertionSubtable
 
     public:
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
+    const InsertionSubtable *table;
+    private:
     unsigned int mark;
     const UnsizedArrayOf<HBGlyphID16> &insertionAction;
   };
@@ -873,11 +919,13 @@ struct InsertionSubtable
 
     driver_context_t dc (this, c);
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
+    }
 
     driver.drive (&dc, c);
 
@@ -935,24 +983,33 @@ struct hb_accelerate_subtables_context_t :
     friend struct hb_aat_layout_lookup_accelerator_t;
 
     public:
-    hb_set_digest_t digest;
+    hb_bit_set_t glyph_set;
+    mutable hb_aat_class_cache_t class_cache;
 
     template <typename T>
     auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN
     (
-      obj_.machine.collect_glyphs (this->digest, num_glyphs)
+      obj_.machine.collect_initial_glyphs (glyph_set, num_glyphs, obj_)
     )
 
     template <typename T>
     void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>)
     {
-      digest = digest.full ();
+      obj_.collect_initial_glyphs (glyph_set, num_glyphs);
     }
 
     template <typename T>
     void init (const T &obj_, unsigned num_glyphs)
     {
+      glyph_set.init ();
       init_ (obj_, num_glyphs, hb_prioritize);
+      class_cache.clear ();
+    }
+
+    void
+    fini ()
+    {
+      glyph_set.fini ();
     }
   };
 
@@ -999,12 +1056,21 @@ struct hb_aat_layout_chain_accelerator_t
     if (unlikely (!thiz))
       return nullptr;
 
+    thiz->count = count;
+
     hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs);
     chain.dispatch (&c_accelerate_subtables);
 
     return thiz;
   }
 
+  void destroy ()
+  {
+    for (unsigned i = 0; i < count; i++)
+      subtables[i].fini ();
+  }
+
+  unsigned count;
   hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
 };
 
@@ -1152,15 +1218,19 @@ struct Chain
     {
       bool reverse;
 
+      auto coverage = subtable->get_coverage ();
+
+      hb_mask_t subtable_flags = subtable->subFeatureFlags;
       if (hb_none (hb_iter (c->range_flags) |
-		   hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); })))
+		   hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); })))
 	goto skip;
-      c->subtable_flags = subtable->subFeatureFlags;
-      c->machine_glyph_set = accel ? accel->subtables[i].digest : hb_set_digest_t::full ();
+      c->subtable_flags = subtable_flags;
+      c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t);
+      c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr;
 
-      if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
+      if (!(coverage & ChainSubtable<Types>::AllDirections) &&
 	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-	  bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical))
+	  bool (coverage & ChainSubtable<Types>::Vertical))
 	goto skip;
 
       /* Buffer contents is always in logical direction.  Determine if
@@ -1190,9 +1260,9 @@ struct Chain
 				(the order opposite that of the characters, which
 				may be right-to-left or left-to-right).
        */
-      reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ?
-		bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) :
-		bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) !=
+      reverse = coverage & ChainSubtable<Types>::Logical ?
+		bool (coverage & ChainSubtable<Types>::Backwards) :
+		bool (coverage & ChainSubtable<Types>::Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
       if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
@@ -1298,6 +1368,12 @@ struct mortmorx
       hb_sanitize_context_t sc;
       this->table = sc.reference_table<T> (face);
 
+      if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
+      {
+        hb_blob_destroy (this->table.get_blob ());
+        this->table = hb_blob_get_empty ();
+      }
+
       this->chain_count = table->get_chain_count ();
 
       this->accels = (hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *) hb_calloc (this->chain_count, sizeof (*accels));
@@ -1311,7 +1387,11 @@ struct mortmorx
     ~accelerator_t ()
     {
       for (unsigned int i = 0; i < this->chain_count; i++)
+      {
+	if (this->accels[i])
+	  this->accels[i]->destroy ();
 	hb_free (this->accels[i]);
+      }
       hb_free (this->accels);
       this->table.destroy ();
     }
@@ -1365,9 +1445,8 @@ struct mortmorx
 
   unsigned get_chain_count () const
   {
-	  return chainCount;
+    return chainCount;
   }
-
   void apply (hb_aat_apply_context_t *c,
 	      const hb_aat_map_t &map,
 	      const accelerator_t &accel) const
@@ -1376,10 +1455,7 @@ struct mortmorx
 
     c->buffer->unsafe_to_concat ();
 
-    if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD)
-      c->buffer_digest = c->buffer->digest ();
-    else
-      c->buffer_digest = hb_set_digest_t::full ();
+    c->setup_buffer_glyph_set ();
 
     c->set_lookup_index (0);
     const Chain<Types> *chain = &firstChain;
@@ -1428,8 +1504,17 @@ struct mortmorx
   DEFINE_SIZE_MIN (8);
 };
 
-struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx> {};
-struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort> {};
+struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx>
+{
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+};
+
+struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort>
+{
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+};
 
 struct morx_accelerator_t : morx::accelerator_t {
   morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {}
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-trak-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-trak-table.hh
index 345a236e9..508606037 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-trak-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout-trak-table.hh
@@ -48,17 +48,69 @@ struct TrackTableEntry
 
   float get_track_value () const { return track.to_float (); }
 
-  int get_value (const void *base, unsigned int index,
-		 unsigned int table_size) const
-  { return (base+valuesZ).as_array (table_size)[index]; }
+  float interpolate_at (unsigned int idx,
+			float ptem,
+			const void *base,
+			hb_array_t<const F16DOT16> size_table) const
+  {
+    const FWORD *values = (base+valuesZ).arrayZ;
+
+    float s0 = size_table[idx].to_float ();
+    float s1 = size_table[idx + 1].to_float ();
+    int v0 = values[idx];
+    int v1 = values[idx + 1];
+
+    // Deal with font bugs.
+    if (unlikely (s1 < s0))
+    { hb_swap (s0, s1); hb_swap (v0, v1); }
+    if (unlikely (ptem < s0)) return v0;
+    if (unlikely (ptem > s1)) return v1;
+    if (unlikely (s0 == s1)) return (v0 + v1) * 0.5f;
+
+    float t = (ptem - s0) / (s1 - s0);
+    return v0 + t * (v1 - v0);
+  }
+
+  float get_value (float ptem,
+		   const void *base,
+		   hb_array_t<const F16DOT16> size_table) const
+  {
+    const FWORD *values = (base+valuesZ).arrayZ;
+
+    unsigned int n_sizes = size_table.length;
+
+    /*
+     * Choose size.
+     */
+    if (!n_sizes) return 0.f;
+    if (n_sizes == 1) return values[0];
+
+    // At least two entries.
+
+    unsigned i;
+    for (i = 0; i < n_sizes; i++)
+      if (size_table[i].to_float () >= ptem)
+	break;
+
+    // Boundary conditions.
+    if (i == 0)       return values[0];
+    if (i == n_sizes) return values[n_sizes - 1];
+
+    // Exact match.
+    if (size_table[i].to_float () == ptem) return values[i];
+
+    // Interpolate.
+    return interpolate_at (i - 1, ptem, base, size_table);
+  }
 
   public:
-  bool sanitize (hb_sanitize_context_t *c, const void *base,
-		 unsigned int table_size) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const void *base,
+		 unsigned int n_sizes) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  (valuesZ.sanitize (c, base, table_size))));
+			  (valuesZ.sanitize (c, base, n_sizes))));
   }
 
   protected:
@@ -76,58 +128,38 @@ struct TrackTableEntry
 
 struct TrackData
 {
-  float interpolate_at (unsigned int idx,
-			float target_size,
-			const TrackTableEntry &trackTableEntry,
-			const void *base) const
+  float get_tracking (const void *base, float ptem, float track = 0.f) const
   {
-    unsigned int sizes = nSizes;
-    hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
+    unsigned count = nTracks;
+    hb_array_t<const F16DOT16> size_table = (base+sizeTable).as_array (nSizes);
 
-    float s0 = size_table[idx].to_float ();
-    float s1 = size_table[idx + 1].to_float ();
-    float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
-    return t * trackTableEntry.get_value (base, idx + 1, sizes) +
-	   (1.f - t) * trackTableEntry.get_value (base, idx, sizes);
-  }
+    if (!count) return 0.f;
+    if (count == 1) return trackTable[0].get_value (ptem, base, size_table);
 
-  int get_tracking (const void *base, float ptem) const
-  {
-    /*
-     * Choose track.
-     */
-    const TrackTableEntry *trackTableEntry = nullptr;
-    unsigned int count = nTracks;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      /* Note: Seems like the track entries are sorted by values.  But the
-       * spec doesn't explicitly say that.  It just mentions it in the example. */
-
-      /* For now we only seek for track entries with zero tracking value */
-
-      if (trackTable[i].get_track_value () == 0.f)
-      {
-	trackTableEntry = &trackTable[i];
-	break;
-      }
-    }
-    if (!trackTableEntry) return 0;
+    // At least two entries.
 
-    /*
-     * Choose size.
-     */
-    unsigned int sizes = nSizes;
-    if (!sizes) return 0;
-    if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
-
-    hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
-    unsigned int size_index;
-    for (size_index = 0; size_index < sizes - 1; size_index++)
-      if (size_table[size_index].to_float () >= ptem)
-	break;
+    unsigned i = 0;
+    unsigned j = count - 1;
+
+    // Find the two entries that track is between.
+    while (i + 1 < count && trackTable[i + 1].get_track_value () < track)
+      i++;
+    while (j > 0 && trackTable[j - 1].get_track_value () > track)
+      j--;
+
+    // Exact match.
+    if (i == j) return trackTable[i].get_value (ptem, base, size_table);
+
+    // Interpolate.
 
-    return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
-				   *trackTableEntry, base));
+    float t0 = trackTable[i].get_track_value ();
+    float t1 = trackTable[j].get_track_value ();
+
+    float t = (track - t0) / (t1 - t0);
+
+    float a = trackTable[i].get_value (ptem, base, size_table);
+    float b = trackTable[j].get_value (ptem, base, size_table);
+    return a + t * (b - a);
   }
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -158,45 +190,15 @@ struct trak
 
   bool has_data () const { return version.to_int (); }
 
-  bool apply (hb_aat_apply_context_t *c) const
+  hb_position_t get_h_tracking (hb_font_t *font, float track = 0.f) const
+  {
+    float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
+    return font->em_scalef_x ((this+horizData).get_tracking (this, ptem, track));
+  }
+  hb_position_t get_v_tracking (hb_font_t *font, float track = 0.f) const
   {
-    TRACE_APPLY (this);
-
-    hb_mask_t trak_mask = c->plan->trak_mask;
-
-    const float ptem = c->font->ptem;
-    if (unlikely (ptem <= 0.f))
-      return_trace (false);
-
-    hb_buffer_t *buffer = c->buffer;
-    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
-    {
-      const TrackData &trackData = this+horizData;
-      int tracking = trackData.get_tracking (this, ptem);
-      hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
-      hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
-      foreach_grapheme (buffer, start, end)
-      {
-	if (!(buffer->info[start].mask & trak_mask)) continue;
-	buffer->pos[start].x_advance += advance_to_add;
-	buffer->pos[start].x_offset += offset_to_add;
-      }
-    }
-    else
-    {
-      const TrackData &trackData = this+vertData;
-      int tracking = trackData.get_tracking (this, ptem);
-      hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
-      hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
-      foreach_grapheme (buffer, start, end)
-      {
-	if (!(buffer->info[start].mask & trak_mask)) continue;
-	buffer->pos[start].y_advance += advance_to_add;
-	buffer->pos[start].y_offset += offset_to_add;
-      }
-    }
-
-    return_trace (true);
+    float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
+    return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.cc
index 9da29e51c..9fe77a575 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.cc
@@ -34,9 +34,12 @@
 #include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
-#include "hb-aat-layout-trak-table.hh"
+#include "hb-aat-layout-trak-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-ltag-table.hh"
 
+#include "hb-ot-layout-gsub-table.hh"
+#include "hb-ot-layout-gdef-table.hh"
+
 
 /*
  * hb_aat_apply_context_t
@@ -207,6 +210,36 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag)
  */
 
 
+bool
+AAT::morx::is_blocklisted (hb_blob_t *blob,
+                           hb_face_t *face) const
+{
+#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
+  return false;
+#endif
+
+  switch HB_CODEPOINT_ENCODE3 (blob->length,
+                               face->table.GSUB->table.get_length (),
+                               face->table.GDEF->table.get_length ())
+  {
+    /* https://github.com/harfbuzz/harfbuzz/issues/4108
+       sha1sum:a71ca6813b7e56a772cffff7c24a5166b087197c  AALMAGHRIBI.ttf */
+    case HB_CODEPOINT_ENCODE3 (19892, 2794, 340):
+      return true;
+  }
+  return false;
+}
+
+bool
+AAT::mort::is_blocklisted (hb_blob_t *blob,
+                           hb_face_t *face) const
+{
+#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
+  return false;
+#endif
+  return false;
+}
+
 void
 hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 			   hb_aat_map_t *map)
@@ -361,17 +394,6 @@ hb_aat_layout_has_tracking (hb_face_t *face)
   return face->table.trak->has_data ();
 }
 
-void
-hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
-		     hb_font_t *font,
-		     hb_buffer_t *buffer)
-{
-  const AAT::trak& trak = *font->face->table.trak;
-
-  AAT::hb_aat_apply_context_t c (plan, font, buffer);
-  trak.apply (&c);
-}
-
 /**
  * hb_aat_layout_get_feature_types:
  * @face: #hb_face_t to work upon
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.hh
index 15c382aa9..c5d6ecb10 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-layout.hh
@@ -32,6 +32,9 @@
 #include "hb-ot-shape.hh"
 #include "hb-aat-ltag-table.hh"
 
+/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
+#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
+
 struct hb_aat_feature_mapping_t
 {
   hb_tag_t otFeatureTag;
@@ -68,10 +71,5 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
 			hb_font_t *font,
 			hb_buffer_t *buffer);
 
-HB_INTERNAL void
-hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
-		     hb_font_t *font,
-		     hb_buffer_t *buffer);
-
 
 #endif /* HB_AAT_LAYOUT_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-map.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-map.cc
index 5bdb8004f..1a0dee022 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-map.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-aat-map.cc
@@ -88,22 +88,23 @@ hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 
   /* Sort features by start/end events. */
   hb_vector_t<feature_event_t> feature_events;
+  feature_events.alloc_exact (features.length * 2 + 1);
   for (unsigned int i = 0; i < features.length; i++)
   {
-    auto &feature = features[i];
+    auto &feature = features.arrayZ[i];
 
-    if (features[i].start == features[i].end)
+    if (feature.start == feature.end)
       continue;
 
     feature_event_t *event;
 
     event = feature_events.push ();
-    event->index = features[i].start;
+    event->index = feature.start;
     event->start = true;
     event->feature = feature.info;
 
     event = feature_events.push ();
-    event->index = features[i].end;
+    event->index = feature.end;
     event->start = false;
     event->feature = feature.info;
   }
@@ -139,12 +140,12 @@ hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 	current_features.qsort ();
 	unsigned int j = 0;
 	for (unsigned int i = 1; i < current_features.length; i++)
-	  if (current_features[i].type != current_features[j].type ||
+	  if (current_features.arrayZ[i].type != current_features.arrayZ[j].type ||
 	      /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
 	       * respectively, so we mask out the low-order bit when checking for "duplicates"
 	       * (selectors referring to the same feature setting) here. */
-	      (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
-	    current_features[++j] = current_features[i];
+	      (!current_features.arrayZ[i].is_exclusive && ((current_features.arrayZ[i].setting & ~1) != (current_features.arrayZ[j].setting & ~1))))
+	    current_features.arrayZ[++j] = current_features.arrayZ[i];
 	current_features.shrink (j + 1);
       }
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
index b02793a09..0252fa7df 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
@@ -286,7 +286,7 @@ HB_FUNCOBJ (hb_bool);
 
 // Compression function for Merkle-Damgard construction.
 // This function is generated using the framework provided.
-#define mix(h) (					\
+#define fasthash_mix(h) (					\
 			(void) ((h) ^= (h) >> 23),		\
 			(void) ((h) *= 0x2127599bf4325c37ULL),	\
 			(h) ^= (h) >> 47)
@@ -310,7 +310,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
 #pragma GCC diagnostic ignored "-Wcast-align"
 	    v  = * (const uint64_t *) (pos++);
 #pragma GCC diagnostic pop
-	    h ^= mix(v);
+	    h ^= fasthash_mix(v);
 	    h *= m;
 	  }
 	}
@@ -320,7 +320,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
 	  while (pos != end)
 	  {
 	    v  = pos++->v;
-	    h ^= mix(v);
+	    h ^= fasthash_mix(v);
 	    h *= m;
 	  }
 	}
@@ -336,11 +336,11 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
 	case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
 	case 2: v ^= (uint64_t)pos2[1] <<  8; HB_FALLTHROUGH;
 	case 1: v ^= (uint64_t)pos2[0];
-		h ^= mix(v);
+		h ^= fasthash_mix(v);
 		h *= m;
 	}
 
-	return mix(h);
+	return fasthash_mix(h);
 }
 
 static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
index 9037179bc..6e458ffd8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
@@ -251,7 +251,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
     if (end < start + 2)
       return;
 
-    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--)
+    unsigned stop = start + (end - start) / 2;
+    for (unsigned lhs = start, rhs = end - 1; lhs < stop; lhs++, rhs--)
       hb_swap (arrayZ[rhs], arrayZ[lhs]);
   }
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-atomic.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-atomic.hh
index 366fb32b7..121c463a5 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-atomic.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-atomic.hh
@@ -212,6 +212,7 @@ struct hb_atomic_ptr_t
   T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
   bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
 
+  operator bool () const { return get_acquire () != nullptr; }
   T * operator -> () const                    { return get_acquire (); }
   template <typename C> operator C * () const { return get_acquire (); }
 
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 869c67895..1941009f8 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh
@@ -78,6 +78,28 @@ struct hb_vector_size_t
   hb_vector_size_t operator ~ () const
   { return process (hb_bitwise_neg); }
 
+  operator bool () const
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      if (v[i])
+	return true;
+    return false;
+  }
+  operator unsigned int () const
+  {
+    unsigned int r = 0;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      r += hb_popcount (v[i]);
+    return r;
+  }
+  bool operator == (const hb_vector_size_t &o) const
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      if (v[i] != o.v[i])
+	return false;
+    return true;
+  }
+
   hb_array_t<const elt_t> iter () const
   { return hb_array (v); }
 
@@ -89,6 +111,8 @@ struct hb_vector_size_t
 
 struct hb_bit_page_t
 {
+  hb_bit_page_t () { init0 (); }
+
   void init0 () { v.init0 (); population = 0; }
   void init1 () { v.init1 (); population = PAGE_BITS; }
 
@@ -101,10 +125,9 @@ struct hb_bit_page_t
   bool is_empty () const
   {
     if (has_population ()) return !population;
-    return
-    + hb_iter (v)
-    | hb_none
-    ;
+    bool empty = !v;
+    if (empty) population = 0;
+    return empty;
   }
   uint32_t hash () const
   {
@@ -115,6 +138,10 @@ struct hb_bit_page_t
   void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); }
   void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
   bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
+
+  bool operator [] (hb_codepoint_t g) const { return get (g); }
+  bool operator () (hb_codepoint_t g) const { return get (g); }
 
   void add_range (hb_codepoint_t a, hb_codepoint_t b)
   {
@@ -220,13 +247,17 @@ struct hb_bit_page_t
   }
 
   bool operator == (const hb_bit_page_t &other) const { return is_equal (other); }
-  bool is_equal (const hb_bit_page_t &other) const
+  bool is_equal (const hb_bit_page_t &other) const { return v == other.v; }
+  bool intersects (const hb_bit_page_t &other) const
   {
     for (unsigned i = 0; i < len (); i++)
-      if (v[i] != other.v[i])
-	return false;
-    return true;
+      if (v[i] & other.v[i])
+	return true;
+    return false;
   }
+  bool may_intersect (const hb_bit_page_t &other) const
+  { return intersects (other); }
+
   bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); }
   bool is_subset (const hb_bit_page_t &larger_page) const
   {
@@ -241,14 +272,10 @@ struct hb_bit_page_t
   }
 
   bool has_population () const { return population != UINT_MAX; }
-  unsigned int get_population () const
+  unsigned get_population () const
   {
     if (has_population ()) return population;
-    population =
-    + hb_iter (v)
-    | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u)
-    ;
-    return population;
+    return population = v;
   }
 
   bool next (hb_codepoint_t *codepoint) const
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 d5d1326d9..034772344 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
@@ -126,6 +126,7 @@ struct hb_bit_set_invertible_t
   { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); }
 
   bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
 
   /* Has interface. */
   bool operator [] (hb_codepoint_t k) const { return get (k); }
@@ -139,6 +140,9 @@ struct hb_bit_set_invertible_t
   hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
   { add_range (range.first, range.second); return *this; }
 
+  bool may_intersect (const hb_bit_set_invertible_t &other) const
+  { return inverted || other.inverted || s.intersects (other.s); }
+
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   {
     hb_codepoint_t c = first - 1;
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 5f4c6f0af..4607b884d 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh
@@ -88,10 +88,11 @@ struct hb_bit_set_t
   {
     if (unlikely (!successful)) return false;
 
-    if (pages.length == 0 && count == 1)
+    if (pages.length < count && count <= 2)
       exact_size = true; // Most sets are small and local
 
-    if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size)))
+    if (unlikely (!pages.resize (count, clear, exact_size) ||
+	!page_map.resize (count, clear)))
     {
       pages.resize (page_map.length, clear, exact_size);
       successful = false;
@@ -297,9 +298,9 @@ struct hb_bit_set_t
       unsigned int write_index = 0;
       for (unsigned int i = 0; i < page_map.length; i++)
       {
-	int m = (int) page_map[i].major;
+	int m = (int) page_map.arrayZ[i].major;
 	if (m < ds || de < m)
-	  page_map[write_index++] = page_map[i];
+	  page_map.arrayZ[write_index++] = page_map.arrayZ[i];
       }
       compact (compact_workspace, write_index);
       resize (write_index);
@@ -345,6 +346,7 @@ struct hb_bit_set_t
       return false;
     return page->get (g);
   }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
 
   /* Has interface. */
   bool operator [] (hb_codepoint_t k) const { return get (k); }
@@ -358,6 +360,31 @@ struct hb_bit_set_t
   hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
   { add_range (range.first, range.second); return *this; }
 
+  bool intersects (const hb_bit_set_t &other) const
+  {
+    unsigned int na = pages.length;
+    unsigned int nb = other.pages.length;
+
+    unsigned int a = 0, b = 0;
+    for (; a < na && b < nb; )
+    {
+      if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
+      {
+	if (page_at (a).intersects (other.page_at (b)))
+	  return true;
+	a++;
+	b++;
+      }
+      else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
+	a++;
+      else
+	b++;
+    }
+    return false;
+  }
+  bool may_intersect (const hb_bit_set_t &other) const
+  { return intersects (other); }
+
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   {
     hb_codepoint_t c = first - 1;
@@ -389,7 +416,7 @@ struct hb_bit_set_t
     {
       if (page_at (a).is_empty ()) { a++; continue; }
       if (other.page_at (b).is_empty ()) { b++; continue; }
-      if (page_map[a].major != other.page_map[b].major ||
+      if (page_map.arrayZ[a].major != other.page_map.arrayZ[b].major ||
 	  !page_at (a).is_equal (other.page_at (b)))
 	return false;
       a++;
@@ -412,8 +439,8 @@ struct hb_bit_set_t
     uint32_t spi = 0;
     for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
     {
-      uint32_t spm = page_map[spi].major;
-      uint32_t lpm = larger_set.page_map[lpi].major;
+      uint32_t spm = page_map.arrayZ[spi].major;
+      uint32_t lpm = larger_set.page_map.arrayZ[lpi].major;
       auto sp = page_at (spi);
 
       if (spm < lpm && !sp.is_empty ())
@@ -503,7 +530,7 @@ struct hb_bit_set_t
 
     for (; a < na && b < nb; )
     {
-      if (page_map[a].major == other.page_map[b].major)
+      if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
       {
 	if (!passthru_left)
 	{
@@ -512,7 +539,7 @@ struct hb_bit_set_t
 	  // passthru_left is set since no left side pages will be removed
 	  // in that case.
 	  if (write_index < a)
-	    page_map[write_index] = page_map[a];
+	    page_map.arrayZ[write_index] = page_map.arrayZ[a];
 	  write_index++;
 	}
 
@@ -520,7 +547,7 @@ struct hb_bit_set_t
 	a++;
 	b++;
       }
-      else if (page_map[a].major < other.page_map[b].major)
+      else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
       {
 	if (passthru_left)
 	  count++;
@@ -765,8 +792,8 @@ struct hb_bit_set_t
     unsigned int initial_size = size;
     for (unsigned int i = start_page; i < page_map.length && size; i++)
     {
-      uint32_t base = major_start (page_map[i].major);
-      unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size);
+      uint32_t base = major_start (page_map.arrayZ[i].major);
+      unsigned int n = pages[page_map.arrayZ[i].index].write (base, start_page_value, out, size);
       out += n;
       size -= n;
       start_page_value = 0;
@@ -814,8 +841,8 @@ struct hb_bit_set_t
     hb_codepoint_t next_value = codepoint + 1;
     for (unsigned int i=start_page; i<page_map.length && size; i++)
     {
-      uint32_t base = major_start (page_map[i].major);
-      unsigned int n = pages[page_map[i].index].write_inverted (base, start_page_value, out, size, &next_value);
+      uint32_t base = major_start (page_map.arrayZ[i].major);
+      unsigned int n = pages[page_map.arrayZ[i].index].write_inverted (base, start_page_value, out, size, &next_value);
       out += n;
       size -= n;
       start_page_value = 0;
@@ -846,8 +873,8 @@ struct hb_bit_set_t
     unsigned count = pages.length;
     for (unsigned i = 0; i < count; i++)
     {
-      const auto& map = page_map[i];
-      const auto& page = pages[map.index];
+      const auto& map = page_map.arrayZ[i];
+      const auto& page = pages.arrayZ[map.index];
 
       if (!page.is_empty ())
 	return map.major * page_t::PAGE_BITS + page.get_min ();
@@ -859,8 +886,8 @@ struct hb_bit_set_t
     unsigned count = pages.length;
     for (signed i = count - 1; i >= 0; i--)
     {
-      const auto& map = page_map[(unsigned) i];
-      const auto& page = pages[map.index];
+      const auto& map = page_map.arrayZ[(unsigned) i];
+      const auto& page = pages.arrayZ[map.index];
 
       if (!page.is_empty ())
 	return map.major * page_t::PAGE_BITS + page.get_max ();
@@ -961,7 +988,7 @@ struct hb_bit_set_t
       return nullptr;
 
     last_page_lookup = i;
-    return &pages.arrayZ[page_map[i].index];
+    return &pages.arrayZ[page_map.arrayZ[i].index];
   }
   page_t &page_at (unsigned int i)
   {
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.hh
index 1deaaafd8..471bbb93f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.hh
@@ -34,36 +34,36 @@
 
 #line 36 "hb-buffer-deserialize-json.hh"
 static const unsigned char _deserialize_json_trans_keys[] = {
-	0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 
-	48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
+	0u, 0u, 9u, 123u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 
+	9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
 	48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
 	9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 
 	34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 
 	9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 
 	9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
-	9u, 123u, 0u, 0u, 0
+	9u, 93u, 9u, 123u, 0u, 0u, 0
 };
 
 static const char _deserialize_json_key_spans[] = {
-	0, 115, 26, 21, 2, 1, 50, 49, 
-	10, 117, 117, 85, 117, 1, 50, 49, 
+	0, 115, 115, 26, 21, 2, 1, 50, 
+	49, 10, 117, 117, 117, 1, 50, 49, 
 	10, 117, 117, 1, 1, 50, 49, 117, 
 	117, 2, 1, 50, 49, 10, 117, 117, 
 	1, 50, 49, 10, 117, 117, 1, 1, 
 	50, 49, 117, 117, 1, 50, 49, 59, 
 	117, 59, 117, 117, 1, 50, 49, 117, 
-	115, 0
+	85, 115, 0
 };
 
 static const short _deserialize_json_index_offsets[] = {
-	0, 0, 116, 143, 165, 168, 170, 221, 
-	271, 282, 400, 518, 604, 722, 724, 775, 
-	825, 836, 954, 1072, 1074, 1076, 1127, 1177, 
-	1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, 
-	1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, 
-	2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, 
-	2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, 
-	3255, 3371
+	0, 0, 116, 232, 259, 281, 284, 286, 
+	337, 387, 398, 516, 634, 752, 754, 805, 
+	855, 866, 984, 1102, 1104, 1106, 1157, 1207, 
+	1325, 1443, 1446, 1448, 1499, 1549, 1560, 1678, 
+	1796, 1798, 1849, 1899, 1910, 2028, 2146, 2148, 
+	2150, 2201, 2251, 2369, 2487, 2489, 2540, 2590, 
+	2650, 2768, 2828, 2946, 3064, 3066, 3117, 3167, 
+	3285, 3371, 3487
 };
 
 static const char _deserialize_json_indicies[] = {
@@ -77,51 +77,51 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 2, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 3, 1, 2, 2, 2, 
+	2, 2, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 2, 1, 3, 3, 3, 
-	3, 3, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 2, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 3, 1, 4, 1, 
-	5, 1, 6, 7, 1, 8, 9, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 10, 1, 11, 12, 
-	1, 13, 1, 13, 13, 13, 13, 13, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 13, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 14, 1, 14, 14, 
-	14, 14, 14, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 14, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 15, 1, 1, 16, 17, 17, 
-	17, 17, 17, 17, 17, 17, 17, 1, 
-	18, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 1, 20, 20, 20, 20, 20, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 20, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 21, 1, 
+	1, 1, 1, 1, 1, 1, 1, 3, 
+	1, 4, 4, 4, 4, 4, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	4, 1, 5, 1, 6, 1, 7, 8, 
+	1, 9, 10, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	11, 1, 12, 13, 1, 14, 1, 14, 
+	14, 14, 14, 14, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 14, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	15, 1, 15, 15, 15, 15, 15, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 22, 
-	1, 23, 23, 23, 23, 23, 1, 1, 
+	1, 15, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 16, 1, 
+	1, 17, 18, 18, 18, 18, 18, 18, 
+	18, 18, 18, 1, 19, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 1, 21, 
+	21, 21, 21, 21, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 21, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	23, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 3, 1, 1, 1, 
+	1, 1, 22, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -131,94 +131,99 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 24, 1, 25, 
-	25, 25, 25, 25, 1, 1, 1, 1, 
+	1, 1, 1, 23, 1, 24, 24, 24, 
+	24, 24, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 25, 1, 
+	1, 1, 1, 1, 24, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 26, 1, 1, 1, 1, 1, 
+	4, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 27, 1, 20, 20, 20, 
-	20, 20, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 20, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	21, 1, 1, 1, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 25, 1, 21, 21, 21, 21, 21, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 21, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 22, 1, 
+	1, 1, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 22, 1, 28, 1, 28, 28, 28, 
-	28, 28, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 28, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 29, 1, 
-	29, 29, 29, 29, 29, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 23, 
+	1, 26, 1, 26, 26, 26, 26, 26, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 29, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 30, 1, 1, 31, 
-	32, 32, 32, 32, 32, 32, 32, 32, 
-	32, 1, 33, 34, 34, 34, 34, 34, 
-	34, 34, 34, 34, 1, 35, 35, 35, 
-	35, 35, 1, 1, 1, 1, 1, 1, 
+	1, 1, 26, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 35, 1, 1, 1, 
+	1, 1, 1, 1, 27, 1, 27, 27, 
+	27, 27, 27, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	36, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 27, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 28, 1, 1, 29, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 1, 
+	31, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 1, 33, 33, 33, 33, 33, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 33, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 34, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 37, 1, 35, 35, 35, 35, 35, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 35, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 36, 1, 
-	1, 1, 34, 34, 34, 34, 34, 34, 
-	34, 34, 34, 34, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 35, 
+	1, 33, 33, 33, 33, 33, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	33, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 34, 1, 1, 1, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 37, 
-	1, 38, 1, 39, 1, 39, 39, 39, 
-	39, 39, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 39, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 40, 1, 
-	40, 40, 40, 40, 40, 1, 1, 1, 
+	1, 1, 1, 1, 1, 35, 1, 36, 
+	1, 37, 1, 37, 37, 37, 37, 37, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 40, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 37, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 38, 1, 38, 38, 
+	38, 38, 38, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 38, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 39, 40, 40, 
+	40, 40, 40, 40, 40, 40, 40, 1, 
+	41, 41, 41, 41, 41, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 41, 
-	42, 42, 42, 42, 42, 42, 42, 42, 
-	42, 1, 43, 43, 43, 43, 43, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 42, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 43, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 44, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -227,15 +232,14 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 43, 1, 41, 41, 
+	41, 41, 41, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 45, 1, 
-	43, 43, 43, 43, 43, 1, 1, 1, 
+	1, 1, 1, 1, 1, 41, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 43, 
+	1, 42, 1, 1, 1, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 44, 1, 1, 1, 46, 
-	46, 46, 46, 46, 46, 46, 46, 46, 
-	46, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -243,26 +247,26 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 45, 1, 47, 48, 
-	1, 49, 1, 49, 49, 49, 49, 49, 
+	1, 1, 43, 1, 45, 46, 1, 47, 
+	1, 47, 47, 47, 47, 47, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 49, 1, 1, 1, 1, 1, 
+	47, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 50, 1, 50, 50, 
-	50, 50, 50, 1, 1, 1, 1, 1, 
+	1, 1, 48, 1, 48, 48, 48, 48, 
+	48, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 50, 1, 1, 
+	1, 1, 1, 48, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 51, 1, 1, 52, 53, 53, 
-	53, 53, 53, 53, 53, 53, 53, 1, 
-	54, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 1, 56, 56, 56, 56, 56, 
+	49, 1, 1, 50, 51, 51, 51, 51, 
+	51, 51, 51, 51, 51, 1, 52, 53, 
+	53, 53, 53, 53, 53, 53, 53, 53, 
+	1, 54, 54, 54, 54, 54, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 56, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 57, 1, 
+	54, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 55, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -272,14 +276,13 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 58, 
-	1, 56, 56, 56, 56, 56, 1, 1, 
+	1, 1, 1, 1, 1, 56, 1, 54, 
+	54, 54, 54, 54, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 54, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	56, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 57, 1, 1, 1, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 1, 1, 1, 1, 1, 1, 
+	1, 1, 55, 1, 1, 1, 53, 53, 
+	53, 53, 53, 53, 53, 53, 53, 53, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -287,119 +290,120 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 58, 1, 59, 
-	1, 59, 59, 59, 59, 59, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 56, 1, 57, 1, 57, 
+	57, 57, 57, 57, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	59, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 57, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	58, 1, 58, 58, 58, 58, 58, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 58, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 59, 1, 
+	1, 60, 61, 61, 61, 61, 61, 61, 
+	61, 61, 61, 1, 62, 63, 63, 63, 
+	63, 63, 63, 63, 63, 63, 1, 64, 
+	64, 64, 64, 64, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 64, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 60, 1, 60, 60, 60, 60, 
-	60, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 65, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 60, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	61, 1, 1, 62, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 1, 64, 65, 
-	65, 65, 65, 65, 65, 65, 65, 65, 
-	1, 66, 66, 66, 66, 66, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	66, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 67, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 66, 1, 64, 64, 64, 
+	64, 64, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 64, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	65, 1, 1, 1, 63, 63, 63, 63, 
+	63, 63, 63, 63, 63, 63, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 68, 1, 66, 
-	66, 66, 66, 66, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 66, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 67, 1, 1, 1, 65, 65, 
-	65, 65, 65, 65, 65, 65, 65, 65, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 66, 1, 67, 1, 68, 1, 68, 
+	68, 68, 68, 68, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 68, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 68, 1, 69, 1, 70, 
-	1, 70, 70, 70, 70, 70, 1, 1, 
+	69, 1, 69, 69, 69, 69, 69, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	70, 1, 1, 1, 1, 1, 1, 1, 
+	1, 69, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 70, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 1, 72, 72, 72, 72, 
+	72, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 71, 1, 71, 71, 71, 71, 
-	71, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 72, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 73, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 71, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 72, 73, 73, 73, 73, 
-	73, 73, 73, 73, 73, 1, 74, 74, 
-	74, 74, 74, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 74, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 75, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	74, 1, 72, 72, 72, 72, 72, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 72, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 73, 1, 1, 
+	1, 75, 75, 75, 75, 75, 75, 75, 
+	75, 75, 75, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 76, 1, 74, 74, 74, 74, 
-	74, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 74, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 75, 
-	1, 1, 1, 77, 77, 77, 77, 77, 
-	77, 77, 77, 77, 77, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 74, 1, 
+	76, 1, 76, 76, 76, 76, 76, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 76, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	76, 1, 78, 1, 78, 78, 78, 78, 
-	78, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 78, 1, 1, 1, 1, 
+	1, 1, 1, 77, 1, 77, 77, 77, 
+	77, 77, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 77, 1, 78, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 79, 1, 79, 
-	79, 79, 79, 79, 1, 1, 1, 1, 
+	1, 1, 1, 1, 79, 80, 80, 80, 
+	80, 80, 80, 80, 80, 80, 1, 82, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 83, 81, 84, 84, 84, 84, 84, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 79, 1, 
-	80, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 81, 82, 
-	82, 82, 82, 82, 82, 82, 82, 82, 
-	1, 84, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 85, 83, 86, 86, 86, 
-	86, 86, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 86, 1, 1, 1, 
+	1, 1, 84, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 85, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	87, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -408,20 +412,21 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 86, 
+	1, 81, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 88, 1, 83, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 81, 1, 87, 87, 87, 
+	87, 87, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 83, 1, 89, 
-	89, 89, 89, 89, 1, 1, 1, 1, 
+	1, 1, 1, 1, 87, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 89, 1, 
+	88, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 90, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -430,97 +435,107 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 89, 1, 87, 87, 87, 87, 87, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 91, 1, 89, 89, 89, 
-	89, 89, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 89, 1, 1, 1, 
+	1, 1, 87, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 88, 1, 
+	1, 1, 90, 90, 90, 90, 90, 90, 
+	90, 90, 90, 90, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	90, 1, 1, 1, 92, 92, 92, 92, 
-	92, 92, 92, 92, 92, 92, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 89, 
+	1, 91, 1, 91, 91, 91, 91, 91, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 91, 1, 93, 1, 93, 93, 93, 
-	93, 93, 1, 1, 1, 1, 1, 1, 
+	1, 1, 91, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 93, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 92, 1, 92, 92, 
+	92, 92, 92, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 94, 1, 
-	94, 94, 94, 94, 94, 1, 1, 1, 
+	1, 1, 1, 1, 1, 92, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 94, 
+	1, 1, 1, 1, 1, 93, 94, 94, 
+	94, 94, 94, 94, 94, 94, 94, 1, 
+	87, 87, 87, 87, 87, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 95, 
-	96, 96, 96, 96, 96, 96, 96, 96, 
-	96, 1, 89, 89, 89, 89, 89, 1, 
+	1, 1, 1, 1, 1, 1, 1, 87, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 88, 1, 1, 1, 95, 
+	95, 95, 95, 95, 95, 95, 95, 95, 
+	95, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 89, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 90, 1, 1, 
-	1, 97, 97, 97, 97, 97, 97, 97, 
-	97, 97, 97, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 89, 1, 96, 96, 
+	96, 96, 96, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 91, 1, 
-	0, 0, 0, 0, 0, 1, 1, 1, 
+	1, 1, 1, 1, 1, 96, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 0, 
+	1, 97, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 98, 1, 2, 2, 2, 2, 
+	2, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 2, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 2, 1, 1, 0
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 3, 1, 
+	1, 0
 };
 
 static const char _deserialize_json_trans_targs[] = {
-	1, 0, 2, 2, 3, 4, 19, 25, 
-	38, 44, 52, 5, 13, 6, 7, 8, 
-	9, 12, 9, 12, 10, 2, 11, 10, 
-	11, 11, 56, 57, 14, 15, 16, 17, 
-	18, 17, 18, 10, 2, 11, 20, 21, 
-	22, 23, 24, 10, 2, 11, 24, 26, 
-	32, 27, 28, 29, 30, 31, 30, 31, 
-	10, 2, 11, 33, 34, 35, 36, 37, 
-	36, 37, 10, 2, 11, 39, 40, 41, 
-	42, 43, 10, 2, 11, 43, 45, 46, 
-	47, 50, 51, 47, 48, 49, 10, 2, 
-	11, 10, 2, 11, 51, 53, 54, 50, 
-	55, 55
+	1, 0, 2, 3, 3, 4, 5, 19, 
+	25, 38, 44, 52, 6, 13, 7, 8, 
+	9, 10, 12, 10, 12, 11, 3, 56, 
+	11, 56, 14, 15, 16, 17, 18, 17, 
+	18, 11, 3, 56, 20, 21, 22, 23, 
+	24, 11, 3, 56, 24, 26, 32, 27, 
+	28, 29, 30, 31, 30, 31, 11, 3, 
+	56, 33, 34, 35, 36, 37, 36, 37, 
+	11, 3, 56, 39, 40, 41, 42, 43, 
+	11, 3, 56, 43, 45, 46, 47, 50, 
+	51, 47, 48, 49, 11, 3, 56, 11, 
+	3, 56, 51, 53, 54, 50, 55, 55, 
+	56, 57, 58
 };
 
 static const char _deserialize_json_trans_actions[] = {
-	0, 0, 1, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 2, 
-	2, 2, 0, 0, 3, 3, 4, 0, 
-	5, 0, 0, 0, 0, 0, 2, 2, 
-	2, 0, 0, 6, 6, 7, 0, 0, 
-	0, 2, 2, 8, 8, 9, 0, 0, 
-	0, 0, 0, 2, 2, 2, 0, 0, 
-	10, 10, 11, 0, 0, 2, 2, 2, 
-	0, 0, 12, 12, 13, 0, 0, 0, 
-	2, 2, 14, 14, 15, 0, 0, 0, 
-	2, 16, 16, 0, 17, 0, 18, 18, 
-	19, 20, 20, 21, 17, 0, 0, 22, 
-	22, 23
+	0, 0, 0, 1, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	2, 2, 2, 0, 0, 3, 3, 4, 
+	0, 5, 0, 0, 2, 2, 2, 0, 
+	0, 6, 6, 7, 0, 0, 0, 2, 
+	2, 8, 8, 9, 0, 0, 0, 0, 
+	0, 2, 2, 2, 0, 0, 10, 10, 
+	11, 0, 0, 2, 2, 2, 0, 0, 
+	12, 12, 13, 0, 0, 0, 2, 2, 
+	14, 14, 15, 0, 0, 0, 2, 16, 
+	16, 0, 17, 0, 18, 18, 19, 20, 
+	20, 21, 17, 0, 0, 22, 22, 23, 
+	0, 0, 0
 };
 
 static const int deserialize_json_start = 1;
@@ -545,22 +560,17 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
   /* Ensure we have positions. */
   (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
-  while (p < pe && ISSPACE (*p))
-    p++;
-  if (p < pe && *p == (buffer->len ? ',' : '['))
-    *end_ptr = ++p;
-
   const char *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
   
-#line 559 "hb-buffer-deserialize-json.hh"
+#line 569 "hb-buffer-deserialize-json.hh"
 	{
 	cs = deserialize_json_start;
 	}
 
-#line 564 "hb-buffer-deserialize-json.hh"
+#line 574 "hb-buffer-deserialize-json.hh"
 	{
 	int _slen;
 	int _trans;
@@ -772,7 +782,7 @@ _resume:
 	*end_ptr = p;
 }
 	break;
-#line 776 "hb-buffer-deserialize-json.hh"
+#line 786 "hb-buffer-deserialize-json.hh"
 	}
 
 _again:
@@ -784,7 +794,7 @@ _again:
 	_out: {}
 	}
 
-#line 137 "hb-buffer-deserialize-json.rl"
+#line 132 "hb-buffer-deserialize-json.rl"
 
 
   *end_ptr = p;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.rl b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.rl
index b12dd0f1a..2f1ad9118 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.rl
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer-deserialize-json.rl
@@ -106,7 +106,7 @@ item	=
 	@add_item
 	;
 
-main := space* item (comma item)* space* (','|']');
+main := space* '['? space* item (comma item)* space* (','|']')?;
 
 }%%
 
@@ -122,11 +122,6 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
   /* Ensure we have positions. */
   (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
-  while (p < pe && ISSPACE (*p))
-    p++;
-  if (p < pe && *p == (buffer->len ? ',' : '['))
-    *end_ptr = ++p;
-
   const char *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc
index 3fc869887..d0c40664a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.cc
@@ -860,7 +860,7 @@ hb_buffer_destroy (hb_buffer_t *buffer)
  * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  *
- * Attaches a user-data key/data pair to the specified buffer. 
+ * Attaches a user-data key/data pair to the specified buffer.
  *
  * Return value: `true` if success, `false` otherwise
  *
@@ -1209,7 +1209,7 @@ hb_buffer_get_flags (const hb_buffer_t *buffer)
  * @cluster_level: The cluster level to set on the buffer
  *
  * Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t
- * dictates one aspect of how HarfBuzz will treat non-base characters 
+ * dictates one aspect of how HarfBuzz will treat non-base characters
  * during shaping.
  *
  * Since: 0.9.42
@@ -1229,7 +1229,7 @@ hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
  * @buffer: An #hb_buffer_t
  *
  * Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t
- * dictates one aspect of how HarfBuzz will treat non-base characters 
+ * dictates one aspect of how HarfBuzz will treat non-base characters
  * during shaping.
  *
  * Return value: The cluster level of @buffer
@@ -1983,7 +1983,7 @@ hb_buffer_add_codepoints (hb_buffer_t          *buffer,
  * @buffer: An #hb_buffer_t
  * @source: source #hb_buffer_t
  * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
- * @end: end index into source buffer to copy.  Use @HB_FEATURE_GLOBAL_END to copy to end of buffer.
+ * @end: end index into source buffer to copy.  Use @UINT_MAX (or ((unsigned int) -1)) to copy to end of buffer.
  *
  * Append (part of) contents of another buffer to this buffer.
  *
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh
index 2a6ad6128..b353b06c7 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-buffer.hh
@@ -32,7 +32,6 @@
 
 #include "hb.hh"
 #include "hb-unicode.hh"
-#include "hb-set-digest.hh"
 
 
 static_assert ((sizeof (hb_glyph_info_t) == 20), "");
@@ -182,22 +181,24 @@ struct hb_buffer_t
     allocated_var_bits = 0;
   }
 
+  HB_ALWAYS_INLINE
   hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
+  HB_ALWAYS_INLINE
   hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
 
+  HB_ALWAYS_INLINE
   hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
+  HB_ALWAYS_INLINE
   hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
 
+  HB_ALWAYS_INLINE
   hb_glyph_info_t &prev ()      { return out_info[out_len ? out_len - 1 : 0]; }
+  HB_ALWAYS_INLINE
   hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
 
-  hb_set_digest_t digest () const
-  {
-    hb_set_digest_t d;
-    d.init ();
-    d.add_array (&info[0].codepoint, len, sizeof (info[0]));
-    return d;
-  }
+  template <typename set_t>
+  void collect_codepoints (set_t &d) const
+  { d.clear (); d.add_array (&info[0].codepoint, len, sizeof (info[0])); }
 
   HB_INTERNAL void similar (const hb_buffer_t &src);
   HB_INTERNAL void reset ();
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 6ca7500af..b0491385f 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
@@ -522,7 +522,7 @@ struct parsed_values_t
 
   void alloc (unsigned n)
   {
-    values.alloc (n, true);
+    values.alloc_exact (n);
   }
 
   void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ())
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh
index 09f669567..40cc2403c 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-config.hh
@@ -157,6 +157,7 @@
 #define HB_NO_FALLBACK_SHAPE
 #define HB_NO_OT_KERN
 #define HB_NO_OT_LAYOUT_BLOCKLIST
+#define HB_NO_AAT_LAYOUT_BLOCKLIST
 #define HB_NO_OT_SHAPE_FALLBACK
 #endif
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-font.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-font.cc
index 92194ea0a..b9726373d 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-font.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-font.cc
@@ -60,11 +60,25 @@ hb_coretext_get_nominal_glyph (hb_font_t *font HB_UNUSED,
 			       void *user_data HB_UNUSED)
 {
   CTFontRef ct_font = (CTFontRef) font_data;
-  UniChar ch = unicode;
-  CGGlyph cg_glyph;
-  if (CTFontGetGlyphsForCharacters (ct_font, &ch, &cg_glyph, 1))
+  UniChar ch[2];
+  CGGlyph cg_glyph[2];
+  unsigned count = 0;
+
+  if (unicode <= 0xFFFF)
+  {
+    ch[count++] = unicode;
+  }
+  else if (unicode <= 0x10FFFF)
   {
-    *glyph = cg_glyph;
+    ch[count++] = (unicode >> 10) + 0xD7C0;
+    ch[count++] = (unicode & 0x3FF) + 0xDC00;
+  }
+  else
+    ch[count++] = 0xFFFD;
+
+  if (CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count))
+  {
+    *glyph = cg_glyph[0];
     return true;
   }
   return false;
@@ -80,6 +94,31 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
 				unsigned int glyph_stride,
 				void *user_data HB_UNUSED)
 {
+  // If any non-BMP codepoint is requested, use the slow path.
+  bool slow_path = false;
+  auto *unicode = first_unicode;
+  for (unsigned i = 0; i < count; i++)
+  {
+    if (*unicode > 0xFFFF)
+    {
+      slow_path = true;
+      break;
+    }
+    unicode = &StructAtOffset<const hb_codepoint_t> (unicode, unicode_stride);
+  }
+
+  if (unlikely (slow_path))
+  {
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (!hb_coretext_get_nominal_glyph (font, font_data, *first_unicode, first_glyph, nullptr))
+	return i;
+      first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    }
+    return count;
+  }
+
   CTFontRef ct_font = (CTFontRef) font_data;
 
   UniChar ch[MAX_GLYPHS];
@@ -92,7 +131,16 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
       ch[j] = *first_unicode;
       first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
     }
-    CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c);
+    if (unlikely (!CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c)))
+    {
+      // Use slow path partially and return at first failure.
+      for (unsigned j = 0; j < c; j++)
+      {
+	if (!hb_coretext_get_nominal_glyph (font, font_data, ch[j], first_glyph, nullptr))
+	  return i + j;
+	first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      }
+    }
     for (unsigned j = 0; j < c; j++)
     {
       *first_glyph = cg_glyph[j];
@@ -113,13 +161,38 @@ hb_coretext_get_variation_glyph (hb_font_t *font HB_UNUSED,
 {
   CTFontRef ct_font = (CTFontRef) font_data;
 
-  UniChar ch[2] = { unicode, variation_selector };
-  CGGlyph cg_glyph[2];
+  UniChar ch[4];
+  CGGlyph cg_glyph[4];
+  unsigned count = 0;
+
+  // Add Unicode, then variation selector. Ugly, but works.
+  //
+  if (unicode <= 0xFFFF)
+    ch[count++] = unicode;
+  else if (unicode <= 0x10FFFF)
+  {
+    ch[count++] = (unicode >> 10) + 0xD7C0;
+    ch[count++] = (unicode & 0x3FF) + 0xDC00;
+  }
+  else
+    ch[count++] = 0xFFFD;
 
-  CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, 2);
+  if (variation_selector <= 0xFFFF)
+    ch[count++] = variation_selector;
+  else if (variation_selector <= 0x10FFFF)
+  {
+    ch[count++] = (variation_selector >> 10) + 0xD7C0;
+    ch[count++] = (variation_selector & 0x3FF) + 0xDC00;
+  }
+  else
+    ch[count++] = 0xFFFD;
 
-  if (cg_glyph[1])
-    return false;
+  CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count);
+
+  // All except for first should be zero if we succeeded
+  for (unsigned i = 1; i < count; i++)
+    if (cg_glyph[i])
+      return false;
 
   *glyph = cg_glyph[0];
   return true;
@@ -438,10 +511,6 @@ _hb_coretext_get_font_funcs ()
  * created with hb_face_create(), and therefore was not
  * initially configured to use CoreText font functions.
  *
- * An #hb_font_t object created with hb_coretext_font_create()
- * is preconfigured for CoreText font functions and does not
- * require this function to be used.
- *
  * <note>Note: Internally, this function creates a CTFont.
 * </note>
  *
@@ -452,7 +521,12 @@ hb_coretext_font_set_funcs (hb_font_t *font)
 {
   CTFontRef ct_font = hb_coretext_font_get_ct_font (font);
   if (unlikely (!ct_font))
+  {
+    hb_font_set_funcs (font,
+		       hb_font_funcs_get_empty (),
+		       nullptr, nullptr);
     return;
+  }
 
   hb_font_set_funcs (font,
 		     _hb_coretext_get_font_funcs (),
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-shape.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-shape.cc
index 73443796d..0bb235f6d 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-shape.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-coretext-shape.cc
@@ -45,9 +45,6 @@
  * Functions for using HarfBuzz with the CoreText fonts.
  **/
 
-/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
-#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
-
 static CTFontRef create_ct_font (CGFontRef cg_font, CGFloat font_size);
 
 static void
@@ -384,9 +381,9 @@ hb_coretext_face_create_from_file_or_fail (const char   *file_name,
 		      (CTFontDescriptorRef) CFArrayGetValueAtIndex (ct_font_desc_array, index) : nullptr;
   if (unlikely (!ct_font_desc))
   {
-	  CFRelease (ct_font_desc_array);
-	  CFRelease (url);
-	  return nullptr;
+    CFRelease (ct_font_desc_array);
+    CFRelease (url);
+    return nullptr;
   }
   CFRelease (url);
   auto ct_font = ct_font_desc ? CTFontCreateWithFontDescriptor (ct_font_desc, 0, nullptr) : nullptr;
@@ -400,6 +397,7 @@ hb_coretext_face_create_from_file_or_fail (const char   *file_name,
     return nullptr;
 
   hb_face_t *face = hb_coretext_face_create (cg_font);
+  CFRelease (cg_font);
   if (unlikely (hb_face_is_immutable (face)))
     return nullptr;
 
@@ -432,7 +430,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
   if (unlikely (!face_data)) return nullptr;
   CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
 
-  CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem);
+  CGFloat font_size = (CGFloat) (font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE);
   CTFontRef ct_font = create_ct_font (cg_font, font_size);
 
   if (unlikely (!ct_font))
@@ -451,11 +449,11 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
 
     for (unsigned i = 0; i < font->num_coords; i++)
     {
-      if (font->coords[i] == 0.) continue;
-
       hb_ot_var_axis_info_t info;
       unsigned int c = 1;
       hb_ot_var_get_axis_infos (font->face, i, &c, &info);
+      if (font->design_coords[i] == info.default_value)
+	continue;
       float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value);
 
       CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag);
@@ -499,7 +497,7 @@ _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
  * CTFontRef.
  *
  * The created font uses the default font functions implemented
- * navitely by HarfBuzz. If you want to use the CoreText font functions
+ * natively by HarfBuzz. If you want to use the CoreText font functions
  * instead (rarely needed), you can do so by calling
  * by hb_coretext_font_set_funcs().
  *
@@ -521,6 +519,36 @@ hb_coretext_font_create (CTFontRef ct_font)
 
   hb_font_set_ptem (font, CTFontGetSize (ct_font));
 
+  /* Copy font variations */
+  CFDictionaryRef variations = CTFontCopyVariation (ct_font);
+  if (variations)
+  {
+    hb_vector_t<hb_variation_t> vars;
+    hb_vector_t<CFTypeRef> keys;
+    hb_vector_t<CFTypeRef> values;
+
+    CFIndex count = CFDictionaryGetCount (variations);
+    if (unlikely (!vars.alloc_exact (count) || !keys.resize_exact (count) || !values.resize_exact (count)))
+      goto done;
+
+    // Fetch them one by one and collect in a vector of our own.
+    CFDictionaryGetKeysAndValues (variations, keys.arrayZ, values.arrayZ);
+    for (CFIndex i = 0; i < count; i++)
+    {
+      int tag;
+      float value;
+      CFNumberGetValue ((CFNumberRef) keys.arrayZ[i], kCFNumberIntType, &tag);
+      CFNumberGetValue ((CFNumberRef) values.arrayZ[i], kCFNumberFloatType, &value);
+
+      hb_variation_t var = {tag, value};
+      vars.push (var);
+    }
+    hb_font_set_variations (font, vars.arrayZ, vars.length);
+
+done:
+    CFRelease (variations);
+  }
+
   /* Let there be dragons here... */
   font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font));
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc
index 6c90265d0..5b191262a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.cc
@@ -29,6 +29,7 @@
 #include "hb-shaper-impl.hh"
 
 #include <dwrite_1.h>
+#include <dwrite_3.h>
 
 #include "hb-directwrite.h"
 
@@ -275,6 +276,8 @@ _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 void
 _hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
 {
+  if (data != HB_SHAPER_DATA_SUCCEEDED)
+    ((IDWriteFont *) (const void *) data)->Release();
 }
 
 
@@ -839,7 +842,7 @@ _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *
 }
 
 static void
-_hb_directwrite_font_release (void *data)
+_hb_directwrite_face_release (void *data)
 {
   if (data)
     ((IDWriteFontFace *) data)->Release ();
@@ -847,7 +850,7 @@ _hb_directwrite_font_release (void *data)
 
 /**
  * hb_directwrite_face_create:
- * @font_face: a DirectWrite IDWriteFontFace object.
+ * @dw_face: a DirectWrite IDWriteFontFace object.
  *
  * Constructs a new face object from the specified DirectWrite IDWriteFontFace.
  *
@@ -856,14 +859,32 @@ _hb_directwrite_font_release (void *data)
  * Since: 2.4.0
  **/
 hb_face_t *
-hb_directwrite_face_create (IDWriteFontFace *font_face)
+hb_directwrite_face_create (IDWriteFontFace *dw_face)
 {
-  if (font_face)
-    font_face->AddRef ();
-  return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
-				    _hb_directwrite_font_release);
+  if (dw_face)
+    dw_face->AddRef ();
+  return hb_face_create_for_tables (_hb_directwrite_reference_table, dw_face,
+				    _hb_directwrite_face_release);
 }
 
+/**
+* hb_directwrite_face_get_dw_font_face:
+* @face: a #hb_face_t object
+*
+* Gets the DirectWrite IDWriteFontFace associated with @face.
+*
+* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
+*
+* Since: 10.4.0
+**/
+IDWriteFontFace *
+hb_directwrite_face_get_dw_font_face (hb_face_t *face)
+{
+  return face->data.directwrite->fontFace;
+}
+
+#ifndef HB_DISABLE_DEPRECATED
+
 /**
 * hb_directwrite_face_get_font_face:
 * @face: a #hb_face_t object
@@ -873,12 +894,90 @@ hb_directwrite_face_create (IDWriteFontFace *font_face)
 * Return value: DirectWrite IDWriteFontFace object corresponding to the given input
 *
 * Since: 2.5.0
+* Deprecated: 10.4.0: Use hb_directwrite_face_get_dw_font_face() instead
 **/
 IDWriteFontFace *
 hb_directwrite_face_get_font_face (hb_face_t *face)
 {
-  return face->data.directwrite->fontFace;
+  return hb_directwrite_face_get_dw_font_face (face);
+}
+
+#endif
+
+/**
+ * hb_directwrite_font_create:
+ * @dw_font: a DirectWrite IDWriteFont object.
+ *
+ * Constructs a new font object from the specified DirectWrite IDWriteFont.
+ *
+ * Return value: #hb_font_t object corresponding to the given input
+ *
+ * Since: 10.3.0
+ **/
+hb_font_t *
+hb_directwrite_font_create (IDWriteFont *dw_font)
+{
+  IDWriteFontFace *dw_face = nullptr;
+  IDWriteFontFace5 *dw_face5 = nullptr;
+
+  if (FAILED (dw_font->CreateFontFace (&dw_face)))
+    return hb_font_get_empty ();
+
+  hb_face_t *face = hb_directwrite_face_create (dw_face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+
+  if (unlikely (hb_object_is_immutable (font)))
+    goto done;
+
+  /* Copy font variations */
+  if (SUCCEEDED (dw_face->QueryInterface (__uuidof (IDWriteFontFace5), (void**) &dw_face5)))
+  {
+    if (dw_face5->HasVariations ())
+    {
+      hb_vector_t<DWRITE_FONT_AXIS_VALUE> values;
+      uint32_t count = dw_face5->GetFontAxisValueCount ();
+      if (likely (values.resize_exact (count)) &&
+	  SUCCEEDED (dw_face5->GetFontAxisValues (values.arrayZ, count)))
+      {
+	hb_vector_t<hb_variation_t> vars;
+	if (likely (vars.resize_exact (count)))
+	{
+	  for (uint32_t i = 0; i < count; ++i)
+	  {
+	    hb_tag_t tag = values[i].axisTag;
+	    float value = values[i].value;
+	    vars[i] = {tag, value};
+	  }
+	  hb_font_set_variations (font, vars.arrayZ, vars.length);
+	}
+      }
+    }
+    dw_face5->Release ();
+  }
+
+  dw_font->AddRef ();
+  font->data.directwrite.cmpexch (nullptr, (hb_directwrite_font_data_t *) dw_font);
+
+done:
+  dw_face->Release ();
+  return font;
 }
 
+/**
+* hb_directwrite_font_get_dw_font:
+* @font: a #hb_font_t object
+*
+* Gets the DirectWrite IDWriteFont associated with @font.
+*
+* Return value: DirectWrite IDWriteFont object corresponding to the given input
+*
+* Since: 10.3.0
+**/
+IDWriteFont *
+hb_directwrite_font_get_dw_font (hb_font_t *font)
+{
+  return (IDWriteFont *) (const void *) font->data.directwrite;
+}
 
 #endif
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.h
index f837627a2..121569c15 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-directwrite.h
@@ -30,11 +30,25 @@
 HB_BEGIN_DECLS
 
 HB_EXTERN hb_face_t *
-hb_directwrite_face_create (IDWriteFontFace *font_face);
+hb_directwrite_face_create (IDWriteFontFace *dw_face);
 
+HB_EXTERN IDWriteFontFace *
+hb_directwrite_face_get_dw_font_face (hb_face_t *face);
+
+HB_EXTERN hb_font_t *
+hb_directwrite_font_create (IDWriteFont *dw_font);
+
+HB_EXTERN IDWriteFont *
+hb_directwrite_font_get_dw_font (hb_font_t *font);
+
+#ifndef HB_DISABLE_DEPRECATED
+
+HB_DEPRECATED_FOR (hb_directwrite_face_get_dw_font_face)
 HB_EXTERN IDWriteFontFace *
 hb_directwrite_face_get_font_face (hb_face_t *face);
 
+#endif
+
 HB_END_DECLS
 
 #endif /* HB_DIRECTWRITE_H */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc
index c7dbf7966..002bb37d4 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.cc
@@ -291,6 +291,7 @@ hb_face_create_or_fail (hb_blob_t    *blob,
   return face;
 }
 
+#ifndef HB_NO_OPEN
 /**
  * hb_face_create_from_file_or_fail:
  * @file_name: A font filename
@@ -317,6 +318,7 @@ hb_face_create_from_file_or_fail (const char   *file_name,
 
   return face;
 }
+#endif
 
 /**
  * hb_face_get_empty:
@@ -491,9 +493,10 @@ hb_face_reference_table (const hb_face_t *face,
  * hb_face_reference_blob:
  * @face: A face object
  *
- * Fetches a pointer to the binary blob that contains the
- * specified face. Returns an empty blob if referencing face data is not
- * possible.
+ * Fetches a pointer to the binary blob that contains the specified face.
+ * If referencing the face data is not possible, this function creates a blob
+ * out of individual table blobs if hb_face_get_table_tags() works with this
+ * face, otherwise it returns an empty blob.
  *
  * Return value: (transfer full): A pointer to the blob for @face
  *
@@ -502,7 +505,41 @@ hb_face_reference_table (const hb_face_t *face,
 hb_blob_t *
 hb_face_reference_blob (hb_face_t *face)
 {
-  return face->reference_table (HB_TAG_NONE);
+  hb_blob_t *blob = face->reference_table (HB_TAG_NONE);
+
+  if (blob == hb_blob_get_empty ())
+  {
+    // If referencing the face blob is not possible (e.g. not implemented by the
+    // font functions), use face builder to create a blob out of individual
+    // table blobs.
+    unsigned total_count = hb_face_get_table_tags (face, 0, nullptr, nullptr);
+    if (total_count)
+    {
+      hb_tag_t tags[64];
+      unsigned count = ARRAY_LENGTH (tags);
+      hb_face_t* builder = hb_face_builder_create ();
+
+      for (unsigned offset = 0; offset < total_count; offset += count)
+      {
+        hb_face_get_table_tags (face, offset, &count, tags);
+	if (unlikely (!count))
+	  break; // Allocation error
+        for (unsigned i = 0; i < count; i++)
+        {
+	  if (unlikely (!tags[i]))
+	    continue;
+	  hb_blob_t *table = hb_face_reference_table (face, tags[i]);
+	  hb_face_builder_add_table (builder, tags[i], table);
+	  hb_blob_destroy (table);
+        }
+      }
+
+      blob = hb_face_reference_blob (builder);
+      hb_face_destroy (builder);
+    }
+  }
+
+  return blob;
 }
 
 /**
@@ -644,6 +681,7 @@ hb_face_set_get_table_tags_func (hb_face_t *face,
   {
     if (destroy)
       destroy (user_data);
+    return;
   }
 
   if (face->get_table_tags_destroy)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h
index 8aec681cf..afc198547 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-face.h
@@ -73,9 +73,14 @@ hb_face_create_from_file_or_fail (const char   *file_name,
  * @tag: the tag of the table to reference
  * @user_data: User data pointer passed by the caller
  *
- * Callback function for hb_face_create_for_tables().
+ * Callback function for hb_face_create_for_tables(). The @tag is the tag of the
+ * table to reference, and the special tag #HB_TAG_NONE is used to reference the
+ * blob of the face itself. If referencing the face blob is not possible, it is
+ * recommended to set hb_get_table_tags_func_t on the @face to allow
+ * hb_face_reference_blob() to create a face blob out of individual table blobs.
  *
- * Return value: (transfer full): A pointer to the @tag table within @face
+ * Return value: (transfer full): A pointer to the @tag table within @face or
+ * `NULL` if the table is not found or cannot be referenced.
  *
  * Since: 0.9.2
  */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft-colr.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft-colr.hh
index 7d8ed4a6f..c96698369 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft-colr.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft-colr.hh
@@ -27,6 +27,7 @@
 
 #include "hb.hh"
 
+#include "hb-decycler.hh"
 #include "hb-paint-extents.hh"
 
 #include FT_COLOR_H
@@ -105,8 +106,8 @@ struct hb_ft_paint_context_t
   FT_Color *palette;
   unsigned palette_index;
   hb_color_t foreground;
-  hb_map_t current_glyphs;
-  hb_map_t current_layers;
+  hb_decycler_t glyphs_decycler;
+  hb_decycler_t layers_decycler;
   int depth_left = HB_MAX_NESTING_LEVEL;
   int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
 };
@@ -218,22 +219,19 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
     case FT_COLR_PAINTFORMAT_COLR_LAYERS:
     {
       FT_OpaquePaint other_paint = {0};
+      hb_decycler_node_t node (c->layers_decycler);
       while (FT_Get_Paint_Layers (ft_face,
 				  &paint.u.colr_layers.layer_iterator,
 				  &other_paint))
       {
-        unsigned i = paint.u.colr_layers.layer_iterator.layer;
-
-	if (unlikely (c->current_layers.has (i)))
+	// FreeType doesn't provide a way to get the layer index, so we use the pointer
+	// for cycle detection.
+	if (unlikely (!node.visit ((uintptr_t) other_paint.p)))
 	  continue;
 
-	c->current_layers.add (i);
-
 	c->funcs->push_group (c->data);
 	c->recurse (other_paint);
 	c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
-
-	c->current_layers.del (i);
       }
     }
     break;
@@ -333,18 +331,16 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
     {
       hb_codepoint_t gid = paint.u.colr_glyph.glyphID;
 
-      if (unlikely (c->current_glyphs.has (gid)))
+      hb_decycler_node_t node (c->glyphs_decycler);
+      if (unlikely (!node.visit (gid)))
 	return;
 
-      c->current_glyphs.add (gid);
-
       c->funcs->push_inverse_root_transform (c->data, c->font);
       c->ft_font->lock.unlock ();
       if (c->funcs->color_glyph (c->data, gid, c->font))
       {
 	c->ft_font->lock.lock ();
 	c->funcs->pop_transform (c->data);
-	c->current_glyphs.del (gid);
 	return;
       }
       c->ft_font->lock.lock ();
@@ -380,8 +376,6 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
 
         if (has_clip_box)
           c->funcs->pop_clip (c->data);
-
-	c->current_glyphs.del (gid);
       }
     }
     break;
@@ -506,7 +500,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
     hb_ft_paint_context_t c (ft_font, font,
 			     paint_funcs, paint_data,
 			     palette, palette_index, foreground);
-    c.current_glyphs.add (gid);
+    hb_decycler_node_t node (c.glyphs_decycler);
+    node.visit (gid);
 
     bool is_bounded = true;
     FT_ClipBox clip_box;
@@ -530,7 +525,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
       hb_ft_paint_context_t ce (ft_font, font,
 			        extents_funcs, &extents_data,
 			        palette, palette_index, foreground);
-      ce.current_glyphs.add (gid);
+      hb_decycler_node_t node2 (ce.glyphs_decycler);
+      node2.visit (gid);
       ce.funcs->push_root_transform (ce.data, font);
       ce.recurse (paint);
       ce.funcs->pop_transform (ce.data);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
index 7e65277d1..efb4b7d24 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
@@ -37,7 +37,11 @@
 #include "hb-draw.hh"
 #include "hb-font.hh"
 #include "hb-machinery.hh"
+#ifndef HB_NO_AAT
+#include "hb-aat-layout-trak-table.hh"
+#endif
 #include "hb-ot-os2-table.hh"
+#include "hb-ot-stat-table.hh"
 #include "hb-ot-shaper-arabic-pua.hh"
 #include "hb-paint.hh"
 
@@ -275,7 +279,7 @@ hb_ft_font_get_load_flags (hb_font_t *font)
 }
 
 /**
- * hb_ft_font_get_face: (skip)
+ * hb_ft_font_get_ft_face: (skip)
  * @font: #hb_font_t to work upon
  *
  * Fetches the FT_Face associated with the specified #hb_font_t
@@ -286,10 +290,10 @@ hb_ft_font_get_load_flags (hb_font_t *font)
  *
  * Return value: (nullable): the FT_Face found or `NULL`
  *
- * Since: 0.9.2
+ * Since: 10.4.0
  **/
 FT_Face
-hb_ft_font_get_face (hb_font_t *font)
+hb_ft_font_get_ft_face (hb_font_t *font)
 {
   if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
     return nullptr;
@@ -299,6 +303,31 @@ hb_ft_font_get_face (hb_font_t *font)
   return ft_font->ft_face;
 }
 
+#ifndef HB_DISABLE_DEPRECATED
+
+/**
+ * hb_ft_font_get_face: (skip)
+ * @font: #hb_font_t to work upon
+ *
+ * Fetches the FT_Face associated with the specified #hb_font_t
+ * font object.
+ *
+ * This function works with #hb_font_t objects created by
+ * hb_ft_font_create() or hb_ft_font_create_referenced().
+ *
+ * Return value: (nullable): the FT_Face found or `NULL`
+ *
+ * Since: 0.9.2
+ * Deprecated: 10.4.0: Use hb_ft_font_get_ft_face() instead.
+ **/
+FT_Face
+hb_ft_font_get_face (hb_font_t *font)
+{
+  return hb_ft_font_get_ft_face (font);
+}
+
+#endif
+
 /**
  * hb_ft_font_lock_face: (skip)
  * @font: #hb_font_t to work upon
@@ -502,6 +531,26 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
   }
+
+#ifndef HB_NO_AAT
+  /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
+#ifndef HB_NO_STYLE
+  bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
+#else
+  bool apply_trak = false;
+#endif
+  if (apply_trak)
+  {
+    hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
+    first_advance = orig_first_advance;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance += tracking;
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+  }
+#endif
 }
 
 #ifndef HB_NO_VERTICAL
@@ -538,7 +587,20 @@ hb_ft_get_glyph_v_advance (hb_font_t *font,
    * have a Y growing upward.  Hence the extra negation. */
 
   hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
-  return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
+  v = ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
+
+#ifndef HB_NO_AAT
+  /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
+#ifndef HB_NO_STYLE
+  bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
+#else
+  bool apply_trak = false;
+#endif
+  if (apply_trak)
+    v += font->face->table.trak->get_v_tracking (font);
+#endif
+
+  return v;
 }
 #endif
 
@@ -1290,7 +1352,7 @@ hb_ft_face_create_cached (FT_Face ft_face)
  *
  * If you know you have valid reasons not to use hb_ft_font_create_referenced(),
  * then it is the client program's responsibility to destroy @ft_face
- * after the #hb_font_t font object has been destroyed.
+ * only after the #hb_font_t font object has been destroyed.
  *
  * HarfBuzz will use the @destroy callback on the #hb_font_t font object
  * if it is supplied when you use this function. However, even if @destroy
@@ -1598,6 +1660,11 @@ _release_blob (void *arg)
 void
 hb_ft_font_set_funcs (hb_font_t *font)
 {
+  // In case of failure...
+  hb_font_set_funcs (font,
+		     hb_font_funcs_get_empty (),
+		     nullptr, nullptr);
+
   hb_blob_t *blob = hb_face_reference_blob (font->face);
   unsigned int blob_length;
   const char *blob_data = hb_blob_get_data (blob, &blob_length);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h
index 8cf14dc39..42bfb4f46 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.h
@@ -111,7 +111,7 @@ HB_EXTERN hb_font_t *
 hb_ft_font_create_referenced (FT_Face ft_face);
 
 HB_EXTERN FT_Face
-hb_ft_font_get_face (hb_font_t *font);
+hb_ft_font_get_ft_face (hb_font_t *font);
 
 HB_EXTERN FT_Face
 hb_ft_font_lock_face (hb_font_t *font);
@@ -142,6 +142,13 @@ hb_ft_hb_font_changed (hb_font_t *font);
 HB_EXTERN void
 hb_ft_font_set_funcs (hb_font_t *font);
 
+#ifndef HB_DISABLE_DEPRECATED
+
+HB_DEPRECATED_FOR (hb_ft_font_get_ft_face)
+HB_EXTERN FT_Face
+hb_ft_font_get_face (hb_font_t *font);
+
+#endif
 
 HB_END_DECLS
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-geometry.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-geometry.hh
index 7777ff9ac..2f7fcb328 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-geometry.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-geometry.hh
@@ -83,6 +83,13 @@ struct hb_transform_t
 		  float x0, float y0) :
     xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
 
+  bool is_identity () const
+  {
+    return xx == 1.f && yx == 0.f &&
+	   xy == 0.f && yy == 1.f &&
+	   x0 == 0.f && y0 == 0.f;
+  }
+
   void multiply (const hb_transform_t &o)
   {
     /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
@@ -201,6 +208,8 @@ struct hb_transform_t
   float y0 = 0.f;
 };
 
+#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}
+
 struct hb_bounds_t
 {
   enum status_t {
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 6655259b7..7437fddfa 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh
@@ -86,21 +86,12 @@ struct IntType
 
     return pb->cmp (*pa);
   }
-  template <typename Type2,
-	    hb_enable_if (std::is_integral<Type2>::value &&
-			  sizeof (Type2) < sizeof (int) &&
-			  sizeof (Type) < sizeof (int))>
-  int cmp (Type2 a) const
-  {
-    Type b = v;
-    return (int) a - (int) b;
-  }
   template <typename Type2,
 	    hb_enable_if (hb_is_convertible (Type2, Type))>
   int cmp (Type2 a) const
   {
     Type b = v;
-    return a < b ? -1 : a == b ? 0 : +1;
+    return (a > b) - (a < b);
   }
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -299,11 +290,6 @@ typedef Index NameID;
 struct VarIdx : HBUINT32 {
   static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu;
   static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, "");
-  static uint32_t add (uint32_t i, unsigned short v)
-  {
-    if (i == NO_VARIATION) return i;
-    return i + v;
-  }
   VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; }
 };
 DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx);
@@ -1747,6 +1733,19 @@ struct TupleValues
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_WORDS)
       {
         if (unlikely (p + run_count * HBINT16::static_size > end)) return false;
+#ifndef HB_OPTIMIZE_SIZE
+        for (; i + 3 < stop; i += 4)
+	{
+	  values.arrayZ[i] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	  values.arrayZ[i + 1] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	  values.arrayZ[i + 2] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	  values.arrayZ[i + 3] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	}
+#endif
         for (; i < stop; i++)
         {
           values.arrayZ[i] = * (const HBINT16 *) p;
@@ -1765,10 +1764,17 @@ struct TupleValues
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_BYTES)
       {
         if (unlikely (p + run_count > end)) return false;
+#ifndef HB_OPTIMIZE_SIZE
+	for (; i + 3 < stop; i += 4)
+	{
+	  values.arrayZ[i] = * (const HBINT8 *) p++;
+	  values.arrayZ[i + 1] = * (const HBINT8 *) p++;
+	  values.arrayZ[i + 2] = * (const HBINT8 *) p++;
+	  values.arrayZ[i + 3] = * (const HBINT8 *) p++;
+	}
+#endif
         for (; i < stop; i++)
-        {
           values.arrayZ[i] = * (const HBINT8 *) p++;
-        }
       }
     }
     return true;
@@ -1777,12 +1783,12 @@ struct TupleValues
   struct iter_t : hb_iter_with_fallback_t<iter_t, int>
   {
     iter_t (const unsigned char *p_, unsigned len_)
-	    : p (p_), end (p_ + len_)
+	    : p (p_), endp (p_ + len_)
     { if (ensure_run ()) read_value (); }
 
     private:
     const unsigned char *p;
-    const unsigned char * const end;
+    const unsigned char * const endp;
     int current_value = 0;
     signed run_count = 0;
     unsigned width = 0;
@@ -1791,7 +1797,7 @@ struct TupleValues
     {
       if (likely (run_count > 0)) return true;
 
-      if (unlikely (p >= end))
+      if (unlikely (p >= endp))
       {
         run_count = 0;
         current_value = 0;
@@ -1810,7 +1816,7 @@ struct TupleValues
 	default: assert (false);
       }
 
-      if (unlikely (p + run_count * width > end))
+      if (unlikely (p + run_count * width > endp))
       {
 	run_count = 0;
 	current_value = 0;
@@ -1837,7 +1843,7 @@ struct TupleValues
     __item_t__ __item__ () const
     { return current_value; }
 
-    bool __more__ () const { return run_count || p < end; }
+    bool __more__ () const { return run_count || p < endp; }
     void __next__ ()
     {
       run_count--;
@@ -1864,10 +1870,146 @@ struct TupleValues
     { return p != o.p || run_count != o.run_count; }
     iter_t __end__ () const
     {
-      iter_t it (end, 0);
+      iter_t it (endp, 0);
       return it;
     }
   };
+
+  struct fetcher_t
+  {
+    fetcher_t (const unsigned char *p_, unsigned len_)
+	      : p (p_), end (p_ + len_) {}
+
+    private:
+    const unsigned char *p;
+    const unsigned char * const end;
+    signed run_count = 0;
+    unsigned width = 0;
+
+    bool ensure_run ()
+    {
+      if (likely (run_count > 0)) return true;
+
+      if (unlikely (p >= end))
+      {
+        run_count = 0;
+	return false;
+      }
+
+      unsigned control = *p++;
+      run_count = (control & VALUE_RUN_COUNT_MASK) + 1;
+      width = control & VALUES_SIZE_MASK;
+      switch (width)
+      {
+        case VALUES_ARE_ZEROS: width = 0; break;
+	case VALUES_ARE_BYTES: width = HBINT8::static_size;  break;
+	case VALUES_ARE_WORDS: width = HBINT16::static_size; break;
+	case VALUES_ARE_LONGS: width = HBINT32::static_size; break;
+	default: assert (false);
+      }
+
+      if (unlikely (p + run_count * width > end))
+      {
+	run_count = 0;
+	return false;
+      }
+
+      return true;
+    }
+
+    void skip (unsigned n)
+    {
+      while (n)
+      {
+	if (unlikely (!ensure_run ()))
+	  return;
+	unsigned i = hb_min (n, (unsigned) run_count);
+	run_count -= i;
+	n -= i;
+	p += i * width;
+      }
+    }
+
+    template <bool scaled>
+    void _add_to (hb_array_t<float> out, float scale = 1.0f)
+    {
+      unsigned n = out.length;
+      float *arrayZ = out.arrayZ;
+
+      for (unsigned i = 0; i < n;)
+      {
+	if (unlikely (!ensure_run ()))
+	  break;
+	unsigned count = hb_min (n - i, (unsigned) run_count);
+	switch (width)
+	{
+	  case 1:
+	  {
+	    const auto *pp = (const HBINT8 *) p;
+	    unsigned j = 0;
+#ifndef HB_OPTIMIZE_SIZE
+	    for (; j + 3 < count; j += 4)
+	    {
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	    }
+#endif
+	    for (; j < count; j++)
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	  }
+	  break;
+	  case 2:
+	  {
+	    const auto *pp = (const HBINT16 *) p;
+	    unsigned j = 0;
+#ifndef HB_OPTIMIZE_SIZE
+	    for (; j + 3 < count; j += 4)
+	    {
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	    }
+#endif
+	    for (; j < count; j++)
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	  }
+	  break;
+	  case 4:
+	  {
+	    const auto *pp = (const HBINT32 *) p;
+	    for (unsigned j = 0; j < count; j++)
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	  }
+	  break;
+	}
+	p += count * width;
+	run_count -= count;
+	i += count;
+      }
+    }
+
+    public:
+    void add_to (hb_array_t<float> out, float scale = 1.0f)
+    {
+      unsigned n = out.length;
+
+      if (scale == 0.0f)
+      {
+        skip (n);
+	return;
+      }
+
+#ifndef HB_OPTIMIZE_SIZE
+      if (scale == 1.0f)
+        _add_to<false> (out);
+      else
+#endif
+        _add_to<true> (out, scale);
+    }
+  };
 };
 
 struct TupleList : CFF2Index
@@ -1877,6 +2019,12 @@ struct TupleList : CFF2Index
     auto bytes = CFF2Index::operator [] (i);
     return TupleValues::iter_t (bytes.arrayZ, bytes.length);
   }
+
+  TupleValues::fetcher_t fetcher (unsigned i) const
+  {
+    auto bytes = CFF2Index::operator [] (i);
+    return TupleValues::fetcher_t (bytes.arrayZ, bytes.length);
+  }
 };
 
 
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 66df28aae..8b666a3d0 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
@@ -553,15 +553,6 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
   return true;
 }
 
-bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
-{
-  funcs->push_clip_glyph (data, glyph, font);
-  funcs->color (data, true, foreground);
-  funcs->pop_clip (data);
-
-  return true;
-}
-
 bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
 {
 #ifdef HB_NO_OT_FONT_CFF
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 b84d896e3..666efeb92 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
@@ -1462,7 +1462,6 @@ struct cff1
     }
 
     HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
-    HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
 
     private:
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 e42217b4e..57023cb91 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
@@ -143,15 +143,6 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
   return true;
 }
 
-bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
-{
-  funcs->push_clip_glyph (data, glyph, font);
-  funcs->color (data, true, foreground);
-  funcs->pop_clip (data);
-
-  return true;
-}
-
 struct cff2_path_param_t
 {
   cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
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 c52c0511c..0074d5bd8 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
@@ -518,7 +518,6 @@ struct cff2
     HB_INTERNAL bool get_extents (hb_font_t *font,
 				  hb_codepoint_t glyph,
 				  hb_glyph_extents_t *extents) const;
-    HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
     HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const;
   };
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face-table-list.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face-table-list.hh
index dd4befffa..97825f4d1 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face-table-list.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face-table-list.hh
@@ -95,9 +95,12 @@ HB_OT_CORE_TABLE (OT, fvar)
 HB_OT_CORE_TABLE (OT, avar)
 HB_OT_CORE_TABLE (OT, cvar)
 HB_OT_ACCELERATOR (OT, gvar)
+#ifndef HB_NO_BEYOND_64K
+HB_OT_ACCELERATOR (OT, GVAR)
+#endif
 HB_OT_CORE_TABLE (OT, MVAR)
 #ifndef HB_NO_VAR_COMPOSITES
-HB_OT_CORE_TABLE (OT, VARC)
+HB_OT_ACCELERATOR (OT, VARC)
 #endif
 #endif
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face.cc
index b0c927979..1cf14f3eb 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-face.cc
@@ -41,6 +41,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-var-varc-table.hh"
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
 
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 7b4724710..edfece170 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-font.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-font.cc
@@ -36,13 +36,16 @@
 #include "hb-ot-face.hh"
 #include "hb-outline.hh"
 
+#ifndef HB_NO_AAT
+#include "hb-aat-layout-trak-table.hh"
+#endif
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-cff2-table.hh"
 #include "hb-ot-cff1-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-post-table.hh"
-#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-stat-table.hh"
 #include "hb-ot-var-varc-table.hh"
 #include "hb-ot-vorg-table.hh"
 #include "OT/Color/CBDT/CBDT.hh"
@@ -73,6 +76,10 @@ struct hb_ot_font_t
 {
   const hb_ot_face_t *ot_face;
 
+#ifndef HB_NO_AAT
+  bool apply_trak;
+#endif
+
 #ifndef HB_NO_OT_FONT_CMAP_CACHE
   hb_ot_font_cmap_cache_t *cmap_cache;
 #endif
@@ -91,6 +98,15 @@ _hb_ot_font_create (hb_font_t *font)
 
   ot_font->ot_face = &font->face->table;
 
+#ifndef HB_NO_AAT
+  /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
+#ifndef HB_NO_STYLE
+  ot_font->apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
+#else
+  ot_font->apply_trak = false;
+#endif
+#endif
+
 #ifndef HB_NO_OT_FONT_CMAP_CACHE
   // retry:
   auto *cmap_cache  = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face,
@@ -200,7 +216,6 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 {
-
   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;
@@ -292,6 +307,20 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
   }
+
+#ifndef HB_NO_AAT
+  if (ot_font->apply_trak)
+  {
+    hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
+    first_advance = orig_first_advance;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance += tracking;
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+  }
+#endif
 }
 
 #ifndef HB_NO_VERTICAL
@@ -356,6 +385,20 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
   }
+
+#ifndef HB_NO_AAT
+  if (ot_font->apply_trak)
+  {
+    hb_position_t tracking = font->face->table.trak->get_v_tracking (font);
+    first_advance = orig_first_advance;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance += tracking;
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+  }
+#endif
 }
 #endif
 
@@ -568,14 +611,11 @@ hb_ot_paint_glyph (hb_font_t *font,
   if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return;
 #endif
 #endif
-#ifndef HB_NO_VAR_COMPOSITES
-  if (font->face->table.VARC->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-#endif
-  if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-#ifndef HB_NO_CFF
-  if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-  if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-#endif
+
+  // Outline glyph
+  paint_funcs->push_clip_glyph (paint_data, glyph, font);
+  paint_funcs->color (paint_data, true, foreground);
+  paint_funcs->pop_clip (paint_data);
 }
 #endif
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hdmx-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hdmx-table.hh
index 8582dbe27..5c187ebbb 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hdmx-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-hdmx-table.hh
@@ -95,7 +95,7 @@ struct hdmx
   bool serialize (hb_serialize_context_t *c,
 		  unsigned version,
 		  Iterator it,
-		  const hb_vector_t<hb_codepoint_pair_t> &new_to_old_gid_list,
+		  hb_array_t<const hb_codepoint_pair_t> new_to_old_gid_list,
 		  unsigned num_glyphs)
   {
     TRACE_SERIALIZE (this);
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 493bc6e7a..8e44f143b 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
@@ -182,7 +182,7 @@ struct hmtxvmtx
 	   hb_requires (hb_is_iterator (Iterator))>
   void serialize (hb_serialize_context_t *c,
 		  Iterator it,
-		  const hb_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+		  hb_array_t<const hb_codepoint_pair_t> new_to_old_gid_list,
 		  unsigned num_long_metrics,
                   unsigned total_num_metrics)
   {
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-kern-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-kern-table.hh
index 2abda78af..d11a913c7 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-kern-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-kern-table.hh
@@ -342,7 +342,7 @@ struct kern
   }
 
   bool apply (AAT::hb_aat_apply_context_t *c,
-	      const AAT::kern_accelerator_data_t *accel_data = nullptr) const
+	      const AAT::kern_accelerator_data_t &accel_data) const
   { return dispatch (c, accel_data); }
 
   template <typename context_t, typename ...Ts>
@@ -395,7 +395,7 @@ struct kern
 
     bool apply (AAT::hb_aat_apply_context_t *c) const
     {
-      return table->apply (c, &accel_data);
+      return table->apply (c, accel_data);
     }
 
     hb_blob_ptr_t<kern> table;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-base-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-base-table.hh
index 68a4e7cba..d98ad6012 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-base-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-base-table.hh
@@ -460,7 +460,7 @@ struct BaseScript
   { return (this+baseValues).get_base_coord (baseline_tag_index); }
 
   bool has_values () const { return baseValues; }
-  bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
+  bool has_min_max () const { return defaultMinMax || baseLangSysRecords; }
 
   void collect_variation_indices (const hb_subset_plan_t* plan,
                                   hb_set_t& varidx_set /* OUT */) const
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 757b05031..f9a5c1b9c 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
@@ -34,6 +34,7 @@
 #include "hb-open-type.hh"
 #include "hb-set.hh"
 #include "hb-bimap.hh"
+#include "hb-cache.hh"
 
 #include "OT/Layout/Common/Coverage.hh"
 #include "OT/Layout/types.hh"
@@ -2076,6 +2077,15 @@ struct ClassDef
     default:return 0;
     }
   }
+  unsigned int get_class (hb_codepoint_t glyph_id,
+			  hb_ot_lookup_cache_t *cache) const
+  {
+    unsigned klass;
+    if (cache && cache->get (glyph_id, &klass)) return klass;
+    klass = get_class (glyph_id);
+    if (cache) cache->set (glyph_id, klass);
+    return klass;
+  }
 
   unsigned get_population () const
   {
@@ -3137,23 +3147,14 @@ struct MultiVarData
   {
     auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices);
 
-    auto values_iter = deltaSets[inner];
-
+    auto values_iter = deltaSets.fetcher (inner);
     unsigned regionCount = regionIndices.len;
-    unsigned count = out.length;
     for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++)
     {
       float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex],
 				       coords, coord_count,
 				       cache);
-      if (scalar == 1.f)
-	for (unsigned i = 0; i < count; i++)
-	  out.arrayZ[i] += *values_iter++;
-      else if (scalar)
-	for (unsigned i = 0; i < count; i++)
-	  out.arrayZ[i] += *values_iter++ * scalar;
-      else
-        values_iter += count;
+      values_iter.add_to (out, scalar);
     }
   }
 
@@ -3439,7 +3440,7 @@ struct MultiItemVariationStore
 {
   using cache_t = SparseVarRegionList::cache_t;
 
-  cache_t *create_cache () const
+  cache_t *create_cache (hb_array_t<float> static_cache = hb_array_t<float> ()) const
   {
 #ifdef HB_NO_VAR
     return nullptr;
@@ -3447,8 +3448,14 @@ struct MultiItemVariationStore
     auto &r = this+regions;
     unsigned count = r.regions.len;
 
-    float *cache = (float *) hb_malloc (sizeof (float) * count);
-    if (unlikely (!cache)) return nullptr;
+    float *cache;
+    if (count <= static_cache.length)
+      cache = static_cache.arrayZ;
+    else
+    {
+      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;
@@ -3456,7 +3463,12 @@ struct MultiItemVariationStore
     return cache;
   }
 
-  static void destroy_cache (cache_t *cache) { hb_free (cache); }
+  static void destroy_cache (cache_t *cache,
+			     hb_array_t<float> static_cache = hb_array_t<float> ())
+  {
+    if (cache != static_cache.arrayZ)
+      hb_free (cache);
+  }
 
   private:
   void get_delta (unsigned int outer, unsigned int inner,
@@ -3731,11 +3743,13 @@ struct ItemVarStoreInstancer
 
   float operator() (uint32_t varIdx, unsigned short offset = 0) const
   {
+   if (!coords || varIdx == VarIdx::NO_VARIATION)
+     return 0.f;
+
+    varIdx += offset;
     if (varIdxMap)
-      varIdx = varIdxMap->map (VarIdx::add (varIdx, offset));
-    else
-      varIdx += offset;
-    return coords ? varStore->get_delta (varIdx, coords, cache) : 0.f;
+      varIdx = varIdxMap->map (varIdx);
+    return varStore->get_delta (varIdx, coords, cache);
   }
 
   const ItemVariationStore *varStore;
@@ -3767,12 +3781,11 @@ struct MultiItemVarStoreInstancer
 
   void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const
   {
-    if (coords)
+    if (coords && varIdx != VarIdx::NO_VARIATION)
     {
+      varIdx += offset;
       if (varIdxMap)
-	varIdx = varIdxMap->map (VarIdx::add (varIdx, offset));
-      else
-	varIdx += offset;
+	varIdx = varIdxMap->map (varIdx);
       varStore->get_delta (varIdx, coords, out, cache);
     }
     else
@@ -3890,8 +3903,8 @@ struct ConditionAxisRange
     {
       // add axisIndex->value into the hashmap so we can check if the record is
       // unique with variations
-      int16_t int_filter_max_val = filterRangeMaxValue.to_int ();
-      int16_t int_filter_min_val = filterRangeMinValue.to_int ();
+      uint16_t int_filter_max_val = (uint16_t) filterRangeMaxValue.to_int ();
+      uint16_t int_filter_min_val = (uint16_t) filterRangeMinValue.to_int ();
       hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val;
 
       condition_map->set (axisIndex, val);
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 966fa06c1..183e08ccc 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
@@ -713,6 +713,7 @@ struct hb_ot_apply_context_t :
   recurse_func_t recurse_func = nullptr;
   const GDEF &gdef;
   const GDEF::accelerator_t &gdef_accel;
+  const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr;
   const ItemVariationStore &var_store;
   ItemVariationStore::cache_t *var_store_cache;
   hb_set_digest_t digest;
@@ -762,10 +763,12 @@ struct hb_ot_apply_context_t :
 					 nullptr
 #endif
 					),
-			digest (buffer_->digest ()),
 			direction (buffer_->props.direction),
 			has_glyph_classes (gdef.has_glyph_classes ())
-  { init_iters (); }
+  {
+    init_iters ();
+    buffer->collect_codepoints (digest);
+  }
 
   ~hb_ot_apply_context_t ()
   {
@@ -899,6 +902,13 @@ struct hb_ot_apply_context_t :
   }
 };
 
+enum class hb_ot_lookup_cache_op_t
+{
+  CREATE,
+  ENTER,
+  LEAVE,
+  DESTROY,
+};
 
 struct hb_accelerate_subtables_context_t :
        hb_dispatch_context_t<hb_accelerate_subtables_context_t>
@@ -923,19 +933,23 @@ struct hb_accelerate_subtables_context_t :
   }
 
   template <typename T>
-  static inline auto cache_func_ (const T *obj, 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, hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; }
+  static inline auto cache_func_ (void *p,
+				  hb_ot_lookup_cache_op_t op,
+				  hb_priority<1>) HB_RETURN (void *, T::cache_func (p, op) )
+  template <typename T=void>
+  static inline void * cache_func_ (void *p,
+				    hb_ot_lookup_cache_op_t op HB_UNUSED,
+				    hb_priority<0>) { return (void *) false; }
   template <typename Type>
-  static inline bool cache_func_to (const void *obj, hb_ot_apply_context_t *c, bool enter)
+  static inline void * cache_func_to (void *p,
+				      hb_ot_lookup_cache_op_t op)
   {
-    const Type *typed_obj = (const Type *) obj;
-    return cache_func_ (typed_obj, c, enter, hb_prioritize);
+    return cache_func_<Type> (p, op, hb_prioritize);
   }
 #endif
 
   typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c);
-  typedef bool (*hb_cache_func_t) (const void *obj, hb_ot_apply_context_t *c, bool enter);
+  typedef void * (*hb_cache_func_t) (void *p, hb_ot_lookup_cache_op_t op);
 
   struct hb_applicable_t
   {
@@ -972,11 +986,11 @@ struct hb_accelerate_subtables_context_t :
     }
     bool cache_enter (hb_ot_apply_context_t *c) const
     {
-      return cache_func (obj, c, true);
+      return (bool) cache_func (c, hb_ot_lookup_cache_op_t::ENTER);
     }
     void cache_leave (hb_ot_apply_context_t *c) const
     {
-      cache_func (obj, c, false);
+      cache_func (c, hb_ot_lookup_cache_op_t::LEAVE);
     }
 #endif
 
@@ -2623,25 +2637,35 @@ struct ContextFormat2_5
     unsigned c = (this+classDef).cost () * ruleSet.len;
     return c >= 4 ? c : 0;
   }
-  bool cache_func (hb_ot_apply_context_t *c, bool enter) const
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
   {
-    if (enter)
+    switch (op)
     {
-      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;
+      case hb_ot_lookup_cache_op_t::CREATE:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::ENTER:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
+	  return (void *) 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 (void *) true;
+      }
+      case hb_ot_lookup_cache_op_t::LEAVE:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	c->new_syllables = (unsigned) -1;
+	HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
+	return nullptr;
+      }
+      case hb_ot_lookup_cache_op_t::DESTROY:
+        return nullptr;
     }
+    return nullptr;
   }
 
   bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@@ -2650,7 +2674,7 @@ struct ContextFormat2_5
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     const ClassDef &class_def = this+classDef;
 
@@ -2836,7 +2860,7 @@ struct ContextFormat3
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     struct ContextApplyLookupContext lookup_context = {
@@ -3650,7 +3674,7 @@ struct ChainContextFormat1_4
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
@@ -3861,28 +3885,37 @@ struct ChainContextFormat2_5
 
   unsigned cache_cost () const
   {
-    unsigned c = (this+lookaheadClassDef).cost () * ruleSet.len;
-    return c >= 4 ? c : 0;
+    return (this+lookaheadClassDef).cost () * ruleSet.len;
   }
-  bool cache_func (hb_ot_apply_context_t *c, bool enter) const
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
   {
-    if (enter)
+    switch (op)
     {
-      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;
+      case hb_ot_lookup_cache_op_t::CREATE:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::ENTER:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
+	  return (void *) 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 (void *) true;
+      }
+      case hb_ot_lookup_cache_op_t::LEAVE:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	c->new_syllables = (unsigned) -1;
+	HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
+	return nullptr;
+      }
+      case hb_ot_lookup_cache_op_t::DESTROY:
+        return nullptr;
     }
+    return nullptr;
   }
 
   bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@@ -3891,7 +3924,7 @@ struct ChainContextFormat2_5
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     const ClassDef &backtrack_class_def = this+backtrackClassDef;
     const ClassDef &input_class_def = this+inputClassDef;
@@ -4137,7 +4170,7 @@ struct ChainContextFormat3
     const auto &input = StructAfter<decltype (inputX)> (backtrack);
 
     unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
     const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
@@ -4408,7 +4441,18 @@ struct hb_ot_layout_lookup_accelerator_t
       thiz->digest.union_ (subtable.digest);
 
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    if (c_accelerate_subtables.cache_user_cost < 4)
+      c_accelerate_subtables.cache_user_idx = (unsigned) -1;
+
     thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx;
+
+    if (thiz->cache_user_idx != (unsigned) -1)
+    {
+      thiz->cache = thiz->subtables[thiz->cache_user_idx].cache_func (nullptr, hb_ot_lookup_cache_op_t::CREATE);
+      if (!thiz->cache)
+	thiz->cache_user_idx = (unsigned) -1;
+    }
+
     for (unsigned i = 0; i < count; i++)
       if (i != thiz->cache_user_idx)
 	thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func;
@@ -4417,6 +4461,17 @@ struct hb_ot_layout_lookup_accelerator_t
     return thiz;
   }
 
+  void fini ()
+  {
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    if (cache)
+    {
+      assert (cache_user_idx != (unsigned) -1);
+      subtables[cache_user_idx].cache_func (cache, hb_ot_lookup_cache_op_t::DESTROY);
+    }
+#endif
+  }
+
   bool may_have (hb_codepoint_t g) const
   { return digest.may_have (g); }
 
@@ -4425,6 +4480,7 @@ struct hb_ot_layout_lookup_accelerator_t
 #endif
   bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const
   {
+    c->lookup_accel = this;
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
     if (use_cache)
     {
@@ -4464,10 +4520,13 @@ struct hb_ot_layout_lookup_accelerator_t
 
 
   hb_set_digest_t digest;
-  private:
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+  public:
+  void *cache = nullptr;
+  private:
   unsigned cache_user_idx = (unsigned) -1;
 #endif
+  private:
   hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
 };
 
@@ -4852,7 +4911,12 @@ struct GSUBGPOS
     ~accelerator_t ()
     {
       for (unsigned int i = 0; i < this->lookup_count; i++)
-	hb_free (this->accels[i]);
+      {
+	auto *accel = this->accels[i].get_relaxed ();
+	if (accel)
+	  accel->fini ();
+	hb_free (accel);
+      }
       hb_free (this->accels);
       this->table.destroy ();
     }
@@ -4873,6 +4937,7 @@ struct GSUBGPOS
 
 	if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel)))
 	{
+	  accel->fini ();
 	  hb_free (accel);
 	  goto retry;
 	}
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 d26f094ba..f7ad72fd4 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.cc
@@ -1923,9 +1923,10 @@ apply_forward (OT::hb_ot_apply_context_t *c,
   while (buffer->idx < buffer->len && buffer->successful)
   {
     bool applied = false;
-    if (accel.digest.may_have (buffer->cur().codepoint) &&
-	(buffer->cur().mask & c->lookup_mask) &&
-	c->check_glyph_property (&buffer->cur(), c->lookup_props))
+    auto &cur = buffer->cur();
+    if (accel.digest.may_have (cur.codepoint) &&
+	(cur.mask & c->lookup_mask) &&
+	c->check_glyph_property (&cur, c->lookup_props))
      {
        applied = accel.apply (c, subtable_count, use_cache);
      }
@@ -1951,9 +1952,10 @@ apply_backward (OT::hb_ot_apply_context_t *c,
   hb_buffer_t *buffer = c->buffer;
   do
   {
-    if (accel.digest.may_have (buffer->cur().codepoint) &&
-	(buffer->cur().mask & c->lookup_mask) &&
-	c->check_glyph_property (&buffer->cur(), c->lookup_props))
+    auto &cur = buffer->cur();
+    if (accel.digest.may_have (cur.codepoint) &&
+	(cur.mask & c->lookup_mask) &&
+	c->check_glyph_property (&cur, c->lookup_props))
       ret |= accel.apply (c, subtable_count, false);
 
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
@@ -2033,7 +2035,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
        * (plus some past glyphs).
        *
        * Only try applying the lookup if there is any overlap. */
-      if (accel->digest.may_have (c.digest))
+      if (accel->digest.may_intersect (c.digest))
       {
 	c.set_lookup_index (lookup_index);
 	c.set_lookup_mask (lookup.mask, false);
@@ -2059,7 +2061,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
       if (stage->pause_func (plan, font, buffer))
       {
 	/* Refresh working buffer digest since buffer changed. */
-	c.digest = buffer->digest ();
+	buffer->collect_codepoints (c.digest);
       }
     }
   }
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 f26bf51ab..52dafbba4 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout.hh
@@ -339,6 +339,11 @@ _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
 	 !_hb_glyph_info_substituted (info);
 }
 static inline void
+_hb_glyph_info_set_default_ignorable (hb_glyph_info_t *info)
+{
+  info->unicode_props() |= UPROPS_MASK_IGNORABLE;
+}
+static inline void
 _hb_glyph_info_clear_default_ignorable (hb_glyph_info_t *info)
 {
   info->unicode_props() &= ~ UPROPS_MASK_IGNORABLE;
@@ -360,7 +365,7 @@ _hb_glyph_info_set_continuation (hb_glyph_info_t *info)
   info->unicode_props() |= UPROPS_MASK_CONTINUATION;
 }
 static inline void
-_hb_glyph_info_reset_continuation (hb_glyph_info_t *info)
+_hb_glyph_info_clear_continuation (hb_glyph_info_t *info)
 {
   info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION;
 }
@@ -633,8 +638,7 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer)
 }
 
 /* Make sure no one directly touches our props... */
-#undef unicode_props0
-#undef unicode_props1
+#undef unicode_props
 #undef lig_props
 #undef glyph_props
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.cc
index fac73eb34..952ab3eb1 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.cc
@@ -390,5 +390,19 @@ hb_ot_map_builder_t::compile (hb_ot_map_t                  &m,
   }
 }
 
+unsigned int hb_ot_map_t::get_feature_tags (unsigned int start_offset, unsigned int *tag_count, hb_tag_t *tags) const
+{
+  if (tag_count)
+  {
+    auto sub_features = features.as_array ().sub_array (start_offset, tag_count);
+    if (tags)
+    {
+      for (unsigned int i = 0; i < sub_features.length; i++)
+        tags[i] = sub_features[i].tag;
+    }
+  }
+
+  return features.length;
+}
 
 #endif
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.hh
index 8af8129ce..185d133d7 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-map.hh
@@ -166,6 +166,9 @@ struct hb_ot_map_t
 			  const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
+  HB_INTERNAL unsigned int get_feature_tags (unsigned int  start_offset,
+					     unsigned int *tag_count, /* IN/OUT */
+					     hb_tag_t     *tags /* OUT */) const;
 
   public:
   hb_tag_t chosen_script[2];
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 59b97a799..c9defc49a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.cc
@@ -46,6 +46,8 @@
 #include "hb-set.hh"
 
 #include "hb-aat-layout.hh"
+#include "hb-ot-stat-table.hh"
+
 
 static inline bool
 _hb_codepoint_is_regional_indicator (hb_codepoint_t u)
@@ -121,10 +123,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t           &plan,
   plan.kern_mask = plan.map.get_mask (kern_tag);
   plan.requested_kerning = !!plan.kern_mask;
 #endif
-#ifndef HB_NO_AAT_SHAPE
-  plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k'));
-  plan.requested_tracking = !!plan.trak_mask;
-#endif
 
   bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
   bool disable_gpos = plan.shaper->gpos_tag &&
@@ -207,9 +205,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t           &plan,
      https://github.com/harfbuzz/harfbuzz/issues/2967. */
   if (plan.apply_morx)
     plan.adjust_mark_positioning_when_zeroing = false;
-
-  /* Currently we always apply trak. */
-  plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
 #endif
 }
 
@@ -274,11 +269,6 @@ hb_ot_shape_plan_t::position (hb_font_t   *font,
 #endif
   else if (this->apply_fallback_kern)
     _hb_ot_shape_fallback_kern (this, font, buffer);
-
-#ifndef HB_NO_AAT_SHAPE
-  if (this->apply_trak)
-    hb_aat_layout_track (this, font, buffer);
-#endif
 }
 
 
@@ -346,13 +336,6 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
   /* Random! */
   map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
 
-#ifndef HB_NO_AAT_SHAPE
-  /* Tracking.  We enable dummy feature here just to allow disabling
-   * AAT 'trak' table using features.
-   * https://github.com/harfbuzz/harfbuzz/issues/1303 */
-  map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK);
-#endif
-
   map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */
   map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
 
@@ -1277,6 +1260,36 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
 }
 
 
+/**
+ * hb_ot_shape_plan_get_feature_tags:
+ * @shape_plan: A shaping plan
+ * @start_offset: The index of first feature to retrieve
+ * @tag_count: (inout): Input = the maximum number of features to return;
+ *                      Output = the actual number of features returned (may be zero)
+ * @tags: (out) (array length=tag_count): The array of enabled feature
+ *
+ * Fetches the list of OpenType feature tags enabled for a shaping plan, if possible.
+ *
+ * Return value: Total number of feature tagss.
+ *
+ * Since: 10.3.0
+ */
+unsigned int
+hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
+				   unsigned int     start_offset,
+				   unsigned int    *tag_count, /* IN/OUT */
+				   hb_tag_t        *tags /* OUT */)
+{
+#ifndef HB_NO_OT_SHAPE
+  return shape_plan->ot.map.get_feature_tags (start_offset, tag_count, tags);
+#else
+  if (tag_count)
+	*tag_count = 0;
+  return 0;
+#endif
+}
+
+
 /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
 static void
 add_char (hb_font_t          *font,
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.h b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.h
index afdff7283..80063f775 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.h
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.h
@@ -48,6 +48,12 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
 				  hb_tag_t         table_tag,
 				  hb_set_t        *lookup_indexes /* OUT */);
 
+HB_EXTERN unsigned int
+hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
+				   unsigned int     start_offset,
+				   unsigned int    *tag_count, /* IN/OUT */
+				   hb_tag_t        *tags /* OUT */);
+
 HB_END_DECLS
 
 #endif /* HB_OT_SHAPE_H */
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 f84aa5c49..791bc6896 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape.hh
@@ -51,7 +51,8 @@ struct hb_ot_shape_plan_key_t
 
   bool equal (const hb_ot_shape_plan_key_t *other)
   {
-    return 0 == hb_memcmp (this, other, sizeof (*this));
+    return variations_index[0] == other->variations_index[0] &&
+	   variations_index[1] == other->variations_index[1];
   }
 };
 
@@ -79,22 +80,12 @@ struct hb_ot_shape_plan_t
 #else
   static constexpr hb_mask_t kern_mask = 0;
 #endif
-#ifndef HB_NO_AAT_SHAPE
-  hb_mask_t trak_mask;
-#else
-  static constexpr hb_mask_t trak_mask = 0;
-#endif
 
 #ifndef HB_NO_OT_KERN
   bool requested_kerning : 1;
 #else
   static constexpr bool requested_kerning = false;
 #endif
-#ifndef HB_NO_AAT_SHAPE
-  bool requested_tracking : 1;
-#else
-  static constexpr bool requested_tracking = false;
-#endif
 #ifndef HB_NO_OT_SHAPE_FRACTIONS
   bool has_frac : 1;
 #else
@@ -117,11 +108,9 @@ struct hb_ot_shape_plan_t
 #ifndef HB_NO_AAT_SHAPE
   bool apply_kerx : 1;
   bool apply_morx : 1;
-  bool apply_trak : 1;
 #else
   static constexpr bool apply_kerx = false;
   static constexpr bool apply_morx = false;
-  static constexpr bool apply_trak = false;
 #endif
 
   void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-fallback.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-fallback.hh
index 66a8bfbd2..b82de35aa 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-fallback.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-arabic-fallback.hh
@@ -355,6 +355,8 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
     if (fallback_plan->lookup_array[i])
     {
+      if (fallback_plan->accel_array[i])
+	fallback_plan->accel_array[i]->fini ();
       hb_free (fallback_plan->accel_array[i]);
       if (fallback_plan->free_lookups)
 	hb_free (fallback_plan->lookup_array[i]);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.cc
index dbe781e56..a48b9b9ae 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shaper-vowel-constraints.cc
@@ -24,7 +24,7 @@ static void
 _output_dotted_circle (hb_buffer_t *buffer)
 {
   (void) buffer->output_glyph (0x25CCu);
-  _hb_glyph_info_reset_continuation (&buffer->prev());
+  _hb_glyph_info_clear_continuation (&buffer->prev());
 }
 
 static void
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 26eb34f5c..3d4ebddd8 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
@@ -6,8 +6,8 @@
  *
  * on files with these headers:
  *
- * <meta name="updated_at" content="2024-12-05 07:13 PM" />
- * File-Date: 2024-11-19
+ * <meta name="updated_at" content="2024-12-06 06:35 AM" />
+ * File-Date: 2025-01-21
  */
 
 #ifndef HB_OT_TAG_TABLE_HH
@@ -745,6 +745,7 @@ static const LangTag ot_languages3[] = {
 /*{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','m',' '),	HB_TAG('Z','H','S',' ')},	/* Hainanese -> Chinese, Simplified */
   {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 */
@@ -981,9 +982,11 @@ static const LangTag ot_languages3[] = {
   {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','h',' '),	HB_TAG('Z','H','S',' ')},	/* Leizhou Chinese -> Chinese, Simplified */
 /*{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','t',' '),	HB_TAG('L','U','T',' ')},*/	/* Lushootseed */
   {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 */
@@ -1404,6 +1407,7 @@ static const LangTag ot_languages3[] = {
   {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','a',' '),	HB_TAG('S','J','A',' ')},*/	/* Epena */
+  {HB_TAG('s','j','c',' '),	HB_TAG('Z','H','S',' ')},	/* Shaojiang Chinese -> Chinese, Simplified */
   {HB_TAG('s','j','d',' '),	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
 /*{HB_TAG('s','j','e',' '),	HB_TAG('S','J','E',' ')},*/	/* Pite Sami */
   {HB_TAG('s','j','o',' '),	HB_TAG('S','I','B',' ')},	/* Xibe -> Sibe */
@@ -2386,6 +2390,26 @@ out:
       *count = i;
       return true;
     }
+    if (lang_matches (&lang_str[1], limit, "nm-hant-hk", 10))
+    {
+      /* Hainanese; 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], limit, "nm-hant-mo", 10))
+    {
+      /* Hainanese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
     if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10))
     {
       /* Xiang Chinese; Han (Traditional variant); Hong Kong */
@@ -2420,6 +2444,20 @@ out:
       *count = 1;
       return true;
     }
+    if (lang_matches (&lang_str[1], limit, "nm-hans", 7))
+    {
+      /* Hainanese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "nm-hant", 7))
+    {
+      /* Hainanese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     if (lang_matches (&lang_str[1], limit, "sn-hans", 7))
     {
       /* Xiang Chinese; Han (Simplified variant) */
@@ -2464,6 +2502,36 @@ out:
       *count = 1;
       return true;
     }
+    if (0 == strncmp (&lang_str[1], "nm-", 3)
+	&& subtag_matches (lang_str, limit, "-hk", 3))
+    {
+      /* Hainanese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "nm-", 3)
+	&& subtag_matches (lang_str, limit, "-mo", 3))
+    {
+      /* Hainanese; Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "nm-", 3)
+	&& subtag_matches (lang_str, limit, "-tw", 3))
+    {
+      /* Hainanese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
 	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
@@ -2525,6 +2593,40 @@ out:
     }
     break;
   case 'l':
+    if (lang_matches (&lang_str[1], limit, "uh-hant-hk", 10))
+    {
+      /* Leizhou 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], limit, "uh-hant-mo", 10))
+    {
+      /* Leizhou Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "uh-hans", 7))
+    {
+      /* Leizhou Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "uh-hant", 7))
+    {
+      /* Leizhou Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
     {
       /* Literary Chinese; Han (Simplified variant) */
@@ -2532,6 +2634,36 @@ out:
       *count = 1;
       return true;
     }
+    if (0 == strncmp (&lang_str[1], "uh-", 3)
+	&& subtag_matches (lang_str, limit, "-hk", 3))
+    {
+      /* Leizhou Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "uh-", 3)
+	&& subtag_matches (lang_str, limit, "-mo", 3))
+    {
+      /* Leizhou Chinese; Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "uh-", 3)
+	&& subtag_matches (lang_str, limit, "-tw", 3))
+    {
+      /* Leizhou Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     break;
   case 'm':
     if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
@@ -2703,6 +2835,72 @@ out:
       return true;
     }
     break;
+  case 's':
+    if (lang_matches (&lang_str[1], limit, "jc-hant-hk", 10))
+    {
+      /* Shaojiang 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], limit, "jc-hant-mo", 10))
+    {
+      /* Shaojiang Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "jc-hans", 7))
+    {
+      /* Shaojiang Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "jc-hant", 7))
+    {
+      /* Shaojiang Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jc-", 3)
+	&& subtag_matches (lang_str, limit, "-hk", 3))
+    {
+      /* Shaojiang Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jc-", 3)
+	&& subtag_matches (lang_str, limit, "-mo", 3))
+    {
+      /* Shaojiang Chinese; Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jc-", 3)
+	&& subtag_matches (lang_str, limit, "-tw", 3))
+    {
+      /* Shaojiang Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
   case 'w':
     if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10))
     {
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-common.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-common.hh
index 3ab58ae30..8eec5195a 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-common.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-common.hh
@@ -231,9 +231,9 @@ struct tuple_delta_t
   /* indices_length = point_count, indice[i] = 1 means point i is referenced */
   hb_vector_t<bool> indices;
 
-  hb_vector_t<double> deltas_x;
+  hb_vector_t<float> deltas_x;
   /* empty for cvar tuples */
-  hb_vector_t<double> deltas_y;
+  hb_vector_t<float> deltas_y;
 
   /* compiled data: header and deltas
    * compiled point data is saved in a hashmap within tuple_variations_t cause
@@ -299,9 +299,9 @@ struct tuple_delta_t
     return *this;
   }
 
-  tuple_delta_t& operator *= (double scalar)
+  tuple_delta_t& operator *= (float scalar)
   {
-    if (scalar == 1.0)
+    if (scalar == 1.0f)
       return *this;
 
     unsigned num = indices.length;
@@ -514,9 +514,9 @@ struct tuple_delta_t
   bool compile_deltas ()
   { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); }
 
-  static bool compile_deltas (const hb_vector_t<bool> &point_indices,
-			      const hb_vector_t<double> &x_deltas,
-			      const hb_vector_t<double> &y_deltas,
+  static bool compile_deltas (hb_array_t<const bool> point_indices,
+			      hb_array_t<const float> x_deltas,
+			      hb_array_t<const float> y_deltas,
 			      hb_vector_t<unsigned char> &compiled_deltas /* OUT */)
   {
     hb_vector_t<int> rounded_deltas;
@@ -629,11 +629,11 @@ struct tuple_delta_t
           deltas_x.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].x,
                                             (double) orig_points.arrayZ[prev].x,
                                             (double) orig_points.arrayZ[next].x,
-                                            deltas_x.arrayZ[prev], deltas_x.arrayZ[next]);
+                                            (double) deltas_x.arrayZ[prev], (double) deltas_x.arrayZ[next]);
           deltas_y.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].y,
                                             (double) orig_points.arrayZ[prev].y,
                                             (double) orig_points.arrayZ[next].y,
-                                            deltas_y.arrayZ[prev], deltas_y.arrayZ[next]);
+                                            (double) deltas_y.arrayZ[prev], (double) deltas_y.arrayZ[next]);
           inferred_idxes.add (i);
           if (--unref_count == 0) goto no_more_gaps;
         }
@@ -692,7 +692,7 @@ struct tuple_delta_t
 
     if (ref_count == count) return true;
 
-    hb_vector_t<double> opt_deltas_x, opt_deltas_y;
+    hb_vector_t<float> opt_deltas_x, opt_deltas_y;
     bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0);
     if (is_comp_glyph_wo_deltas)
     {
@@ -841,6 +841,7 @@ struct tuple_delta_t
   { return (i >= end) ? start : (i + 1); }
 };
 
+template <typename OffType = HBUINT16>
 struct TupleVariationData
 {
   bool sanitize (hb_sanitize_context_t *c) const
@@ -875,7 +876,7 @@ struct TupleVariationData
 
     private:
     /* referenced point set->compiled point data map */
-    hb_hashmap_t<const hb_vector_t<bool>*, hb_vector_t<char>> point_data_map;
+    hb_hashmap_t<const hb_vector_t<bool>*, hb_vector_t<unsigned char>> point_data_map;
     /* referenced point set-> count map, used in finding shared points */
     hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map;
 
@@ -883,7 +884,7 @@ struct TupleVariationData
      * shared_points_bytes is a pointer to some value in the point_data_map,
      * which will be freed during map destruction. Save it for serialization, so
      * no need to do find_shared_points () again */
-    hb_vector_t<char> *shared_points_bytes = nullptr;
+    hb_vector_t<unsigned char> *shared_points_bytes = nullptr;
 
     /* total compiled byte size as TupleVariationData format, initialized to 0 */
     unsigned compiled_byte_size = 0;
@@ -1244,7 +1245,7 @@ struct TupleVariationData
       for (auto& tuple: tuple_vars)
       {
         const hb_vector_t<bool>* points_set = &(tuple.indices);
-        hb_vector_t<char> *points_data;
+        hb_vector_t<unsigned char> *points_data;
         if (unlikely (!point_data_map.has (points_set, &points_data)))
           return false;
 
@@ -1289,20 +1290,20 @@ struct TupleVariationData
       TRACE_SERIALIZE (this);
       if (is_gvar && shared_points_bytes)
       {
-        hb_bytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length);
+        hb_ubytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length);
         s.copy (c);
       }
 
       for (const auto& tuple: tuple_vars)
       {
         const hb_vector_t<bool>* points_set = &(tuple.indices);
-        hb_vector_t<char> *point_data;
+        hb_vector_t<unsigned char> *point_data;
         if (!point_data_map.has (points_set, &point_data))
           return_trace (false);
 
         if (!is_gvar || point_data != shared_points_bytes)
         {
-          hb_bytes_t s (point_data->arrayZ, point_data->length);
+          hb_ubytes_t s (point_data->arrayZ, point_data->length);
           s.copy (c);
         }
 
@@ -1521,15 +1522,16 @@ struct TupleVariationData
                                  * low 12 bits are the number of tuple variation tables
                                  * for this glyph. The number of tuple variation tables
                                  * can be any number between 1 and 4095. */
-  Offset16To<HBUINT8>
+  OffsetTo<HBUINT8, OffType>
                 data;           /* Offset from the start of the base table
                                  * to the serialized data. */
   /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */
   public:
-  DEFINE_SIZE_MIN (4);
+  DEFINE_SIZE_MIN (2 + OffType::static_size);
 };
 
-using tuple_variations_t = TupleVariationData::tuple_variations_t;
+// TODO: Move tuple_variations_t to outside of TupleVariationData
+using tuple_variations_t = TupleVariationData<HBUINT16>::tuple_variations_t;
 struct item_variations_t
 {
   using region_t = const hb_hashmap_t<hb_tag_t, Triple>*;
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-cvar-table.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-cvar-table.hh
index 3931382f1..f8ae0c803 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-cvar-table.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-cvar-table.hh
@@ -50,7 +50,7 @@ struct cvar
 		  tupleVariationData.sanitize (c));
   }
 
-  const TupleVariationData* get_tuple_var_data (void) const
+  const TupleVariationData<>* get_tuple_var_data (void) const
   { return &tupleVariationData; }
 
   bool decompile_tuple_variations (unsigned axis_count,
@@ -58,12 +58,12 @@ struct cvar
                                    hb_blob_t *blob,
                                    bool is_gvar,
                                    const hb_map_t *axes_old_index_tag_map,
-                                   TupleVariationData::tuple_variations_t& tuple_variations /* OUT */) const
+                                   TupleVariationData<>::tuple_variations_t& tuple_variations /* OUT */) const
   {
     hb_vector_t<unsigned> shared_indices;
-    TupleVariationData::tuple_iterator_t iterator;
+    TupleVariationData<>::tuple_iterator_t iterator;
     hb_bytes_t var_data_bytes = blob->as_bytes ().sub_array (4);
-    if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, this,
+    if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, this,
                                                  shared_indices, &iterator))
       return false;
     
@@ -77,16 +77,16 @@ struct cvar
   static bool calculate_cvt_deltas (unsigned axis_count,
                                     hb_array_t<int> coords,
                                     unsigned num_cvt_item,
-                                    const TupleVariationData *tuple_var_data,
+                                    const TupleVariationData<> *tuple_var_data,
                                     const void *base,
                                     hb_vector_t<float>& cvt_deltas /* OUT */)
   {
     if (!coords) return true;
     hb_vector_t<unsigned> shared_indices;
-    TupleVariationData::tuple_iterator_t iterator;
+    TupleVariationData<>::tuple_iterator_t iterator;
     unsigned var_data_length = tuple_var_data->get_size (axis_count);
     hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast<const char*> (tuple_var_data), var_data_length);
-    if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, base,
+    if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, base,
                                                  shared_indices, &iterator))
       return true; /* isn't applied at all */
 
@@ -107,14 +107,14 @@ struct cvar
 
       bool has_private_points = iterator.current_tuple->has_private_points ();
       if (has_private_points &&
-          !TupleVariationData::decompile_points (p, private_indices, end))
+          !TupleVariationData<>::decompile_points (p, private_indices, end))
         return false;
       const hb_vector_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices;
 
       bool apply_to_all = (indices.length == 0);
       unsigned num_deltas = apply_to_all ? num_cvt_item : indices.length;
       if (unlikely (!unpacked_deltas.resize (num_deltas, false))) return false;
-      if (unlikely (!TupleVariationData::decompile_deltas (p, unpacked_deltas, end))) return false;
+      if (unlikely (!TupleVariationData<>::decompile_deltas (p, unpacked_deltas, end))) return false;
 
       for (unsigned int i = 0; i < num_deltas; i++)
       {
@@ -129,7 +129,7 @@ struct cvar
   }
   
   bool serialize (hb_serialize_context_t *c,
-                  TupleVariationData::tuple_variations_t& tuple_variations) const
+                  TupleVariationData<>::tuple_variations_t& tuple_variations) const
   {
     TRACE_SERIALIZE (this);
     if (!tuple_variations) return_trace (false);
@@ -144,7 +144,7 @@ struct cvar
     if (c->plan->all_axes_pinned)
       return_trace (false);
 
-    OT::TupleVariationData::tuple_variations_t tuple_variations;
+    OT::TupleVariationData<>::tuple_variations_t tuple_variations;
     unsigned axis_count = c->plan->axes_old_index_tag_map.get_population ();
 
     const hb_tag_t cvt = HB_TAG('c','v','t',' ');
@@ -169,7 +169,7 @@ struct cvar
   }
 
   static bool add_cvt_and_apply_deltas (hb_subset_plan_t *plan,
-                                        const TupleVariationData *tuple_var_data,
+                                        const TupleVariationData<> *tuple_var_data,
                                         const void *base)
   {
     const hb_tag_t cvt = HB_TAG('c','v','t',' ');
@@ -209,7 +209,7 @@ struct cvar
   protected:
   FixedVersion<>version;		/* Version of the CVT variation table
 					 * initially set to 0x00010000u */
-  TupleVariationData tupleVariationData; /* TupleVariationDate for cvar table */
+  TupleVariationData<> tupleVariationData; /* TupleVariationDate for cvar table */
   public:
   DEFINE_SIZE_MIN (8);
 };
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 96cc2e887..b388c7143 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
@@ -28,6 +28,7 @@
 #ifndef HB_OT_VAR_GVAR_TABLE_HH
 #define HB_OT_VAR_GVAR_TABLE_HH
 
+#include "hb-decycler.hh"
 #include "hb-open-type.hh"
 #include "hb-ot-var-common.hh"
 
@@ -36,15 +37,37 @@
  * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar
  */
 #define HB_OT_TAG_gvar HB_TAG('g','v','a','r')
+#define HB_OT_TAG_GVAR HB_TAG('G','V','A','R')
 
-namespace OT {
+struct hb_glyf_scratch_t
+{
+  // glyf
+  contour_point_vector_t all_points;
+  contour_point_vector_t comp_points;
+  hb_decycler_t decycler;
+
+  // gvar
+  contour_point_vector_t orig_points;
+  hb_vector_t<int> x_deltas;
+  hb_vector_t<int> y_deltas;
+  contour_point_vector_t deltas;
+  hb_vector_t<unsigned int> shared_indices;
+  hb_vector_t<unsigned int> private_indices;
+
+  // VARC
+  hb_vector_t<unsigned> axisIndices;
+  hb_vector_t<float> axisValues;
+};
 
-struct GlyphVariationData : TupleVariationData
-{};
+namespace OT {
 
+template <typename OffsetType>
 struct glyph_variations_t
 {
-  using tuple_variations_t = TupleVariationData::tuple_variations_t;
+  // TODO: Move tuple_variations_t to outside of TupleVariationData
+  using tuple_variations_t = typename TupleVariationData<OffsetType>::tuple_variations_t;
+  using GlyphVariationData = TupleVariationData<OffsetType>;
+
   hb_vector_t<tuple_variations_t> glyph_variations;
 
   hb_vector_t<char> compiled_shared_tuples;
@@ -72,7 +95,7 @@ struct glyph_variations_t
                                     const hb_subset_plan_t *plan,
                                     const hb_hashmap_t<hb_codepoint_t, hb_bytes_t>& new_gid_var_data_map)
   {
-    if (unlikely (!glyph_variations.alloc (plan->new_to_old_gid_list.length, true)))
+    if (unlikely (!glyph_variations.alloc_exact (plan->new_to_old_gid_list.length)))
       return false;
 
     auto it = hb_iter (plan->new_to_old_gid_list);
@@ -86,10 +109,11 @@ struct glyph_variations_t
       hb_bytes_t var_data = new_gid_var_data_map.get (new_gid);
 
       const GlyphVariationData* p = reinterpret_cast<const GlyphVariationData*> (var_data.arrayZ);
-      hb_vector_t<unsigned> shared_indices;
-      GlyphVariationData::tuple_iterator_t iterator;
+      typename GlyphVariationData::tuple_iterator_t iterator;
       tuple_variations_t tuple_vars;
 
+      hb_vector_t<unsigned> shared_indices;
+
       /* in case variation data is empty, push an empty struct into the vector,
        * keep the vector in sync with the new_to_old_gid_list */
       if (!var_data || ! p->has_data () || !all_contour_points->length ||
@@ -259,7 +283,7 @@ struct glyph_variations_t
     hb_codepoint_t last_gid = 0;
     unsigned idx = 0;
 
-    TupleVariationData* cur_glyph = c->start_embed<TupleVariationData> ();
+    GlyphVariationData* cur_glyph = c->start_embed<GlyphVariationData> ();
     if (!cur_glyph) return_trace (false);
     for (auto &_ : it)
     {
@@ -273,7 +297,7 @@ struct glyph_variations_t
 
       if (idx >= glyph_variations.length) return_trace (false);
       if (!cur_glyph->serialize (c, true, glyph_variations[idx])) return_trace (false);
-      TupleVariationData* next_glyph = c->start_embed<TupleVariationData> ();
+      GlyphVariationData* next_glyph = c->start_embed<GlyphVariationData> ();
       glyph_offset += (char *) next_glyph - (char *) cur_glyph;
 
       if (long_offset)
@@ -296,9 +320,14 @@ struct glyph_variations_t
   }
 };
 
-struct gvar
+template <typename GidOffsetType, unsigned TableTag>
+struct gvar_GVAR
 {
-  static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar;
+  static constexpr hb_tag_t tableTag = TableTag;
+
+  using GlyphVariationData = TupleVariationData<GidOffsetType>;
+
+  bool has_data () const { return version.to_int () != 0; }
 
   bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
@@ -317,7 +346,7 @@ struct gvar
   { return sanitize_shallow (c); }
 
   bool decompile_glyph_variations (hb_subset_context_t *c,
-                                   glyph_variations_t& glyph_vars /* OUT */) const
+                                   glyph_variations_t<GidOffsetType>& glyph_vars /* OUT */) const
   {
     hb_hashmap_t<hb_codepoint_t, hb_bytes_t> new_gid_var_data_map;
     auto it = hb_iter (c->plan->new_to_old_gid_list);
@@ -344,14 +373,14 @@ struct gvar
   template<typename Iterator,
            hb_requires (hb_is_iterator (Iterator))>
   bool serialize (hb_serialize_context_t *c,
-                  const glyph_variations_t& glyph_vars,
+                  const glyph_variations_t<GidOffsetType>& glyph_vars,
                   Iterator it,
                   unsigned axis_count,
                   unsigned num_glyphs,
                   bool force_long_offsets) const
   {
     TRACE_SERIALIZE (this);
-    gvar *out = c->allocate_min<gvar> ();
+    gvar_GVAR *out = c->allocate_min<gvar_GVAR> ();
     if (unlikely (!out)) return_trace (false);
 
     out->version.major = 1;
@@ -393,7 +422,7 @@ struct gvar
   bool instantiate (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    glyph_variations_t glyph_vars;
+    glyph_variations_t<GidOffsetType> glyph_vars;
     if (!decompile_glyph_variations (c, glyph_vars))
       return_trace (false);
 
@@ -423,7 +452,7 @@ struct gvar
 
     unsigned glyph_count = version.to_int () ? c->plan->source->get_num_glyphs () : 0;
 
-    gvar *out = c->serializer->allocate_min<gvar> ();
+    gvar_GVAR *out = c->serializer->allocate_min<gvar_GVAR> ();
     if (unlikely (!out)) return_trace (false);
 
     out->version.major = 1;
@@ -557,9 +586,11 @@ struct gvar
   public:
   struct accelerator_t
   {
+    bool has_data () const { return table->has_data (); }
+
     accelerator_t (hb_face_t *face)
     {
-      table = hb_sanitize_context_t ().reference_table<gvar> (face);
+      table = hb_sanitize_context_t ().reference_table<gvar_GVAR> (face);
       /* If sanitize failed, set glyphCount to 0. */
       glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0;
 
@@ -627,35 +658,40 @@ struct gvar
     bool apply_deltas_to_points (hb_codepoint_t glyph,
 				 hb_array_t<const int> coords,
 				 const hb_array_t<contour_point_t> points,
+				 hb_glyf_scratch_t &scratch,
 				 bool phantom_only = false) const
     {
       if (unlikely (glyph >= glyphCount)) return true;
 
       hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyphCount, glyph);
       if (!var_data_bytes.as<GlyphVariationData> ()->has_data ()) return true;
-      hb_vector_t<unsigned int> shared_indices;
-      GlyphVariationData::tuple_iterator_t iterator;
+
+      auto &shared_indices = scratch.shared_indices;
+      shared_indices.clear ();
+
+      typename GlyphVariationData::tuple_iterator_t iterator;
       if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount,
 						   var_data_bytes.arrayZ,
 						   shared_indices, &iterator))
 	return true; /* so isn't applied at all */
 
       /* Save original points for inferred delta calculation */
-      contour_point_vector_t orig_points_vec; // Populated lazily
+      auto &orig_points_vec = scratch.orig_points;
+      orig_points_vec.clear (); // Populated lazily
       auto orig_points = orig_points_vec.as_array ();
 
       /* flag is used to indicate referenced point */
-      contour_point_vector_t deltas_vec; // Populated lazily
+      auto &deltas_vec = scratch.deltas;
+      deltas_vec.clear (); // Populated lazily
       auto deltas = deltas_vec.as_array ();
 
-      hb_vector_t<unsigned> end_points; // Populated lazily
-
       unsigned num_coords = table->axisCount;
       hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * num_coords);
 
-      hb_vector_t<unsigned int> private_indices;
-      hb_vector_t<int> x_deltas;
-      hb_vector_t<int> y_deltas;
+      auto &private_indices = scratch.private_indices;
+      auto &x_deltas = scratch.x_deltas;
+      auto &y_deltas = scratch.y_deltas;
+
       unsigned count = points.length;
       bool flush = false;
       do
@@ -726,8 +762,8 @@ struct gvar
 	    if (phantom_only && pt_index < count - 4) continue;
 	    auto &delta = deltas.arrayZ[pt_index];
 	    delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
-	    delta.x += x_deltas.arrayZ[i] * scalar;
-	    delta.y += y_deltas.arrayZ[i] * scalar;
+	    delta.add_delta (x_deltas.arrayZ[i] * scalar,
+			     y_deltas.arrayZ[i] * scalar);
 	  }
 	}
 	else
@@ -738,10 +774,9 @@ struct gvar
 	    if (apply_to_all)
 	      for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
 	      {
-		unsigned int pt_index = i;
-		auto &delta = deltas.arrayZ[pt_index];
-		delta.x += x_deltas.arrayZ[i] * scalar;
-		delta.y += y_deltas.arrayZ[i] * scalar;
+		auto &delta = deltas.arrayZ[i];
+		delta.add_delta (x_deltas.arrayZ[i] * scalar,
+				 y_deltas.arrayZ[i] * scalar);
 	      }
 	    else
 	      for (unsigned int i = 0; i < num_deltas; i++)
@@ -751,8 +786,8 @@ struct gvar
 		if (phantom_only && pt_index < count - 4) continue;
 		auto &delta = deltas.arrayZ[pt_index];
 		delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
-		delta.x += x_deltas.arrayZ[i] * scalar;
-		delta.y += y_deltas.arrayZ[i] * scalar;
+		delta.add_delta (x_deltas.arrayZ[i] * scalar,
+				 y_deltas.arrayZ[i] * scalar);
 	      }
 	  }
 	  else
@@ -760,10 +795,9 @@ struct gvar
 	    if (apply_to_all)
 	      for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
 	      {
-		unsigned int pt_index = i;
-		auto &delta = deltas.arrayZ[pt_index];
-		delta.x += x_deltas.arrayZ[i];
-		delta.y += y_deltas.arrayZ[i];
+		auto &delta = deltas.arrayZ[i];
+		delta.add_delta (x_deltas.arrayZ[i],
+				 y_deltas.arrayZ[i]);
 	      }
 	    else
 	      for (unsigned int i = 0; i < num_deltas; i++)
@@ -773,8 +807,8 @@ struct gvar
 		if (phantom_only && pt_index < count - 4) continue;
 		auto &delta = deltas.arrayZ[pt_index];
 		delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
-		delta.x += x_deltas.arrayZ[i];
-		delta.y += y_deltas.arrayZ[i];
+		delta.add_delta (x_deltas.arrayZ[i],
+				 y_deltas.arrayZ[i]);
 	      }
 	  }
 	}
@@ -782,17 +816,14 @@ struct gvar
 	/* infer deltas for unreferenced points */
 	if (!apply_to_all && !phantom_only)
 	{
-	  if (!end_points)
-	  {
-	    for (unsigned i = 0; i < count; ++i)
-	      if (points.arrayZ[i].is_end_point)
-		end_points.push (i);
-	    if (unlikely (end_points.in_error ())) return false;
-	  }
-
 	  unsigned start_point = 0;
-	  for (unsigned end_point : end_points)
+	  unsigned end_point = 0;
+	  while (true)
 	  {
+	    while (end_point < count && !points.arrayZ[end_point].is_end_point)
+	      end_point++;
+	    if (unlikely (end_point == count)) break;
+
 	    /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */
 	    unsigned unref_count = 0;
 	    for (unsigned i = start_point; i < end_point + 1; i++)
@@ -835,7 +866,7 @@ struct gvar
 	      }
 	    }
 	  no_more_gaps:
-	    start_point = end_point + 1;
+	    start_point = end_point = end_point + 1;
 	  }
 	}
 
@@ -855,7 +886,7 @@ struct gvar
     unsigned int get_axis_count () const { return table->axisCount; }
 
     private:
-    hb_blob_ptr_t<gvar> table;
+    hb_blob_ptr_t<gvar_GVAR> table;
     unsigned glyphCount;
     hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx;
   };
@@ -873,7 +904,7 @@ struct gvar
   NNOffset32To<UnsizedArrayOf<F2DOT14>>
 		sharedTuples;	/* Offset from the start of this table to the shared tuple records.
 				 * Array of tuple records shared across all glyph variation data tables. */
-  HBUINT16	glyphCountX;	/* The number of glyphs in this font. This must match the number of
+  GidOffsetType	glyphCountX;	/* The number of glyphs in this font. This must match the number of
 				 * glyphs stored elsewhere in the font. */
   HBUINT16	flags;		/* Bit-field that gives the format of the offset array that follows.
 				 * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the
@@ -888,9 +919,15 @@ struct gvar
   DEFINE_SIZE_ARRAY (20, offsetZ);
 };
 
+using gvar = gvar_GVAR<HBUINT16, HB_OT_TAG_gvar>;
+using GVAR = gvar_GVAR<HBUINT24, HB_OT_TAG_GVAR>;
+
 struct gvar_accelerator_t : gvar::accelerator_t {
   gvar_accelerator_t (hb_face_t *face) : gvar::accelerator_t (face) {}
 };
+struct GVAR_accelerator_t : GVAR::accelerator_t {
+  GVAR_accelerator_t (hb_face_t *face) : GVAR::accelerator_t (face) {}
+};
 
 } /* namespace OT */
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
index f066d0e31..63ab18586 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
@@ -78,11 +78,11 @@ struct hb_serialize_context_t
       head = o.head;
       tail = o.tail;
       next = nullptr;
-      real_links.alloc (o.num_real_links, true);
+      real_links.alloc_exact (o.num_real_links);
       for (unsigned i = 0 ; i < o.num_real_links; i++)
         real_links.push (o.real_links[i]);
 
-      virtual_links.alloc (o.num_virtual_links, true);
+      virtual_links.alloc_exact (o.num_virtual_links);
       for (unsigned i = 0; i < o.num_virtual_links; i++)
         virtual_links.push (o.virtual_links[i]);
     }
@@ -172,7 +172,7 @@ struct hb_serialize_context_t
     auto all_links () const HB_AUTO_RETURN
         (( hb_concat (real_links, virtual_links) ));
     auto all_links_writer () HB_AUTO_RETURN
-        (( hb_concat (real_links.writer (), virtual_links.writer ()) ));           
+        (( hb_concat (real_links.writer (), virtual_links.writer ()) ));
   };
 
   struct snapshot_t
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 b718b94e6..0d05e72c1 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-set-digest.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-set-digest.hh
@@ -56,7 +56,7 @@
  *   - For each glyph, if it doesn't match the subtable digest,
  *     skip it.
  *
- * The main filter we use is a combination of three bits-pattern
+ * The main filter we use is a combination of four 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.
@@ -64,45 +64,60 @@
  * check is done using four bitwise operations only.
  */
 
-template <typename mask_t, unsigned int shift>
-struct hb_set_digest_bits_pattern_t
+static constexpr unsigned hb_set_digest_shifts[] = {4, 0, 6};
+
+struct hb_set_digest_t
 {
+  // No science in these. Intuition and testing only.
+  using mask_t = uint64_t;
+
+  static constexpr unsigned n = ARRAY_LENGTH_CONST (hb_set_digest_shifts);
   static constexpr unsigned mask_bytes = sizeof (mask_t);
   static constexpr unsigned mask_bits = sizeof (mask_t) * 8;
-  static constexpr unsigned num_bits = 0
-				     + (mask_bytes >= 1 ? 3 : 0)
-				     + (mask_bytes >= 2 ? 1 : 0)
-				     + (mask_bytes >= 4 ? 1 : 0)
-				     + (mask_bytes >= 8 ? 1 : 0)
-				     + (mask_bytes >= 16? 1 : 0)
-				     + 0;
+  static constexpr hb_codepoint_t mb1 = mask_bits - 1;
+  static constexpr mask_t one = 1;
+  static constexpr mask_t all = (mask_t) -1;
 
-  static_assert ((shift < sizeof (hb_codepoint_t) * 8), "");
-  static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), "");
-
-  void init () { mask = 0; }
+  void init ()
+  { for (unsigned i = 0; i < n; i++) masks[i] = 0; }
 
-  static hb_set_digest_bits_pattern_t full () { hb_set_digest_bits_pattern_t d; d.mask = (mask_t) -1; return d; }
+  void clear () { init (); }
 
-  void union_ (const hb_set_digest_bits_pattern_t &o) { mask |= o.mask; }
+  static hb_set_digest_t full ()
+  {
+    hb_set_digest_t d;
+    for (unsigned i = 0; i < n; i++) d.masks[i] = all;
+    return d;
+  }
 
-  void add (hb_codepoint_t g) { mask |= mask_for (g); }
+  void union_ (const hb_set_digest_t &o)
+  { for (unsigned i = 0; i < n; i++) masks[i] |= o.masks[i]; }
 
   bool add_range (hb_codepoint_t a, hb_codepoint_t b)
   {
-    if (mask == (mask_t) -1) return false;
-    if ((b >> shift) - (a >> shift) >= mask_bits - 1)
-    {
-      mask = (mask_t) -1;
-      return false;
-    }
-    else
+    bool ret;
+
+    ret = false;
+    for (unsigned i = 0; i < n; i++)
+      if (masks[i] != all)
+	ret = true;
+    if (!ret) return false;
+
+    ret = false;
+    for (unsigned i = 0; i < n; i++)
     {
-      mask_t ma = mask_for (a);
-      mask_t mb = mask_for (b);
-      mask |= mb + (mb - ma) - (mb < ma);
-      return true;
+      mask_t shift = hb_set_digest_shifts[i];
+      if ((b >> shift) - (a >> shift) >= mb1)
+	masks[i] = all;
+      else
+      {
+	mask_t ma = one << ((a >> shift) & mb1);
+	mask_t mb = one << ((b >> shift) & mb1);
+	masks[i] |= mb + (mb - ma) - (mb < ma);
+	ret = true;
+      }
     }
+    return ret;
   }
 
   template <typename T>
@@ -125,103 +140,37 @@ struct hb_set_digest_bits_pattern_t
   template <typename T>
   bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
 
-  bool may_have (const hb_set_digest_bits_pattern_t &o) const
-  { return mask & o.mask; }
-
-  bool may_have (hb_codepoint_t g) const
-  { return mask & mask_for (g); }
-
   bool operator [] (hb_codepoint_t g) const
   { return may_have (g); }
 
-  private:
-
-  static mask_t mask_for (hb_codepoint_t g)
-  { return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); }
-  mask_t mask = 0;
-};
-
-template <typename head_t, typename tail_t>
-struct hb_set_digest_combiner_t
-{
-  void init ()
-  {
-    head.init ();
-    tail.init ();
-  }
-
-  static hb_set_digest_combiner_t full () { hb_set_digest_combiner_t d; d.head = head_t::full(); d.tail = tail_t::full (); return d; }
-
-  void union_ (const hb_set_digest_combiner_t &o)
-  {
-    head.union_ (o.head);
-    tail.union_(o.tail);
-  }
 
   void add (hb_codepoint_t g)
   {
-    head.add (g);
-    tail.add (g);
-  }
-
-  bool add_range (hb_codepoint_t a, hb_codepoint_t b)
-  {
-    return (int) head.add_range (a, b) | (int) tail.add_range (a, b);
-  }
-  template <typename T>
-  void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
-  {
-    head.add_array (array, count, stride);
-    tail.add_array (array, count, stride);
-  }
-  template <typename T>
-  void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); }
-  template <typename T>
-  bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
-  {
-    return head.add_sorted_array (array, count, stride) &&
-	   tail.add_sorted_array (array, count, stride);
+    for (unsigned i = 0; i < n; i++)
+      masks[i] |= one << ((g >> hb_set_digest_shifts[i]) & mb1);
   }
-  template <typename T>
-  bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
 
-  bool may_have (const hb_set_digest_combiner_t &o) const
+  HB_ALWAYS_INLINE
+  bool may_have (hb_codepoint_t g) const
   {
-    return head.may_have (o.head) && tail.may_have (o.tail);
+    for (unsigned i = 0; i < n; i++)
+      if (!(masks[i] & (one << ((g >> hb_set_digest_shifts[i]) & mb1))))
+	return false;
+    return true;
   }
 
-  bool may_have (hb_codepoint_t g) const
+  bool may_intersect (const hb_set_digest_t &o) const
   {
-    return head.may_have (g) && tail.may_have (g);
+    for (unsigned i = 0; i < n; i++)
+      if (!(masks[i] & o.masks[i]))
+	return false;
+    return true;
   }
 
-  bool operator [] (hb_codepoint_t g) const
-  { return may_have (g); }
-
   private:
-  head_t head;
-  tail_t tail;
-};
-
 
-/*
- * hb_set_digest_t
- *
- * This is a combination of digests that performs "best".
- * There is not much science to this: it's a result of intuition
- * and testing.
- */
-using hb_set_digest_t =
-  hb_set_digest_combiner_t
-  <
-    hb_set_digest_bits_pattern_t<unsigned long, 4>,
-    hb_set_digest_combiner_t
-    <
-      hb_set_digest_bits_pattern_t<unsigned long, 0>,
-      hb_set_digest_bits_pattern_t<unsigned long, 9>
-    >
-  >
-;
+  mask_t masks[n] = {};
+};
 
 
 #endif /* HB_SET_DIGEST_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
index f6013a414..f098bd4c1 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
@@ -106,6 +106,7 @@ struct hb_sparseset_t
   void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); }
 
   bool get (hb_codepoint_t g) const { return s.get (g); }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
 
   /* Has interface. */
   bool operator [] (hb_codepoint_t k) const { return get (k); }
@@ -120,6 +121,9 @@ struct hb_sparseset_t
   hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
   { add_range (range.first, range.second); return *this; }
 
+  bool may_intersect (const hb_sparseset_t &other) const
+  { return s.may_intersect (other.s); }
+
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   { return s.intersects (first, last); }
 
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 312eeb653..47eb690a7 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-shape-plan.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-shape-plan.cc
@@ -209,7 +209,7 @@ hb_shape_plan_create (hb_face_t                     *face,
  * @num_coords: The number of variation-space coordinates
  * @shaper_list: (array zero-terminated=1): List of shapers to try
  *
- * The variable-font version of #hb_shape_plan_create. 
+ * The variable-font version of #hb_shape_plan_create.
  * Constructs a shaping plan for a combination of @face, @user_features, @props,
  * and @shaper_list, plus the variation-space coordinates @coords.
  *
@@ -233,7 +233,7 @@ hb_shape_plan_create2 (hb_face_t                     *face,
 		  num_coords,
 		  shaper_list);
 
-  if (unlikely (props->direction == HB_DIRECTION_INVALID))
+  if (unlikely (!HB_DIRECTION_IS_VALID (props->direction)))
     return hb_shape_plan_get_empty ();
 
   hb_shape_plan_t *shape_plan;
@@ -331,7 +331,7 @@ hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
  * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  *
- * Attaches a user-data key/data pair to the given shaping plan. 
+ * Attaches a user-data key/data pair to the given shaping plan.
  *
  * Return value: `true` if success, `false` otherwise.
  *
@@ -352,7 +352,7 @@ hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
  * @shape_plan: A shaping plan
  * @key: The user-data key to query
  *
- * Fetches the user data associated with the specified key, 
+ * Fetches the user data associated with the specified key,
  * attached to the specified shaping plan.
  *
  * Return value: (transfer none): A pointer to the user data
@@ -501,7 +501,7 @@ hb_shape_plan_create_cached (hb_face_t                     *face,
  * @num_coords: The number of variation-space coordinates
  * @shaper_list: (array zero-terminated=1): List of shapers to try
  *
- * The variable-font version of #hb_shape_plan_create_cached. 
+ * The variable-font version of #hb_shape_plan_create_cached.
  * Creates a cached shaping plan suitable for reuse, for a combination
  * of @face, @user_features, @props, and @shaper_list, plus the
  * variation-space coordinates @coords.
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 4039f9c95..c3f7b40c8 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
@@ -1128,7 +1128,7 @@ struct subr_subsetter_t
       if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr)
         size += 3;
     }
-    if (!buff.alloc (buff.length + size, true))
+    if (!buff.alloc_exact (buff.length + size))
       return false;
 
     for (auto &opstr : str.values)
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 e9dd5d642..d080e0a27 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc
@@ -45,7 +45,7 @@ struct remap_sid_t
   void alloc (unsigned size)
   {
     map.alloc (size);
-    vector.alloc (size, true);
+    vector.alloc_exact (size);
   }
 
   bool in_error () const
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 fe80c08bc..214ea9341 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh
@@ -95,7 +95,6 @@ struct contour_point_t
   HB_ALWAYS_INLINE
   void translate (const contour_point_t &p) { x += p.x; y += p.y; }
 
-
   float x;
   float y;
   uint8_t flag;
@@ -104,19 +103,9 @@ struct contour_point_t
 
 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;
-    if (unlikely (!resize (old_len + a.length, false)))
-      return;
-    auto arrayZ = this->arrayZ + old_len;
-    unsigned count = a.length;
-    hb_memcpy (arrayZ, a.arrayZ, count * sizeof (arrayZ[0]));
-  }
-
-  bool add_deltas (const hb_vector_t<float> deltas_x,
-                   const hb_vector_t<float> deltas_y,
-                   const hb_vector_t<bool> indices)
+  bool add_deltas (hb_array_t<const float> deltas_x,
+                   hb_array_t<const float> deltas_y,
+                   hb_array_t<const bool> indices)
   {
     if (indices.length != deltas_x.length ||
         indices.length != deltas_y.length)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
index 4e96c9853..fbdf1b4f9 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
@@ -296,7 +296,7 @@ _try_subset (const TableType *table,
              HB_UNTAG (c->table_tag), buf_size);
 
   if (unlikely (buf_size > c->source_blob->length * 256 ||
-		!buf->alloc (buf_size, true)))
+		!buf->alloc_exact (buf_size)))
   {
     DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.",
                HB_UNTAG (c->table_tag), buf_size);
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
index c0cc7063f..0e01d010f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
@@ -53,28 +53,25 @@ struct hb_vector_t
   }
   template <typename Iterable,
 	    hb_requires (hb_is_iterable (Iterable))>
-  hb_vector_t (const Iterable &o) : hb_vector_t ()
+  explicit hb_vector_t (const Iterable &o) : hb_vector_t ()
   {
-    auto iter = hb_iter (o);
-    if (iter.is_random_access_iterator || iter.has_fast_len)
-      alloc (hb_len (iter), true);
-    hb_copy (iter, *this);
+    extend (o);
   }
   hb_vector_t (const hb_vector_t &o) : hb_vector_t ()
   {
-    alloc (o.length, true);
+    alloc_exact (o.length);
     if (unlikely (in_error ())) return;
     copy_array (o.as_array ());
   }
   hb_vector_t (array_t o) : hb_vector_t ()
   {
-    alloc (o.length, true);
+    alloc_exact (o.length);
     if (unlikely (in_error ())) return;
     copy_array (o);
   }
   hb_vector_t (c_array_t o) : hb_vector_t ()
   {
-    alloc (o.length, true);
+    alloc_exact (o.length);
     if (unlikely (in_error ())) return;
     copy_array (o);
   }
@@ -87,6 +84,35 @@ struct hb_vector_t
   }
   ~hb_vector_t () { fini (); }
 
+  template <typename Iterable,
+	    hb_requires (hb_is_iterable (Iterable))>
+  void extend (const Iterable &o)
+  {
+    auto iter = hb_iter (o);
+    if (iter.is_random_access_iterator || iter.has_fast_len)
+      alloc (hb_len (iter), true);
+    while (iter)
+    {
+      if (unlikely (!alloc (length + 1)))
+        return;
+      unsigned room = allocated - length;
+      for (unsigned i = 0; i < room && iter; i++)
+	push_has_room (*iter++);
+    }
+  }
+  void extend (array_t o)
+  {
+    alloc (length + o.length);
+    if (unlikely (in_error ())) return;
+    copy_array (o);
+  }
+  void extend (c_array_t o)
+  {
+    alloc (length + o.length);
+    if (unlikely (in_error ())) return;
+    copy_array (o);
+  }
+
   public:
   int allocated = 0; /* < 0 means allocation failed. */
   unsigned int length = 0;
@@ -132,9 +158,10 @@ struct hb_vector_t
   hb_vector_t& operator = (const hb_vector_t &o)
   {
     reset ();
-    alloc (o.length, true);
+    alloc_exact (o.length);
     if (unlikely (in_error ())) return *this;
 
+    length = 0;
     copy_array (o.as_array ());
 
     return *this;
@@ -218,6 +245,10 @@ struct hb_vector_t
       // reference to it.
       return std::addressof (Crap (Type));
 
+    return push_has_room (std::forward<Args> (args)...);
+  }
+  template <typename... Args> Type *push_has_room (Args&&... args)
+  {
     /* Emplace. */
     Type *p = std::addressof (arrayZ[length++]);
     return new (p) Type (std::forward<Args> (args)...);
@@ -311,18 +342,23 @@ struct hb_vector_t
     length = size;
   }
 
+  template <typename T = Type,
+	    hb_enable_if (hb_is_trivially_copyable (T))>
+  void
+  copy_array (hb_array_t<Type> other)
+  {
+    assert ((int) (length + other.length) <= allocated);
+    hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size);
+    length += other.length;
+  }
   template <typename T = Type,
 	    hb_enable_if (hb_is_trivially_copyable (T))>
   void
   copy_array (hb_array_t<const Type> other)
   {
-    length = other.length;
-    if (!HB_OPTIMIZE_SIZE_VAL && sizeof (T) >= sizeof (long long))
-      /* This runs faster because of alignment. */
-      for (unsigned i = 0; i < length; i++)
-	arrayZ[i] = other.arrayZ[i];
-    else
-       hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size);
+    assert ((int) (length + other.length) <= allocated);
+    hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size);
+    length += other.length;
   }
   template <typename T = Type,
 	    hb_enable_if (!hb_is_trivially_copyable (T) &&
@@ -330,12 +366,10 @@ struct hb_vector_t
   void
   copy_array (hb_array_t<const Type> other)
   {
-    length = 0;
-    while (length < other.length)
-    {
-      length++;
-      new (std::addressof (arrayZ[length - 1])) Type (other.arrayZ[length - 1]);
-    }
+    assert ((int) (length + other.length) <= allocated);
+    for (unsigned i = 0; i < other.length; i++)
+      new (std::addressof (arrayZ[length + i])) Type (other.arrayZ[i]);
+    length += other.length;
   }
   template <typename T = Type,
 	    hb_enable_if (!hb_is_trivially_copyable (T) &&
@@ -345,13 +379,13 @@ struct hb_vector_t
   void
   copy_array (hb_array_t<const Type> other)
   {
-    length = 0;
-    while (length < other.length)
+    assert ((int) (length + other.length) <= allocated);
+    for (unsigned i = 0; i < other.length; i++)
     {
-      length++;
-      new (std::addressof (arrayZ[length - 1])) Type ();
-      arrayZ[length - 1] = other.arrayZ[length - 1];
+      new (std::addressof (arrayZ[length + i])) Type ();
+      arrayZ[length + i] = other.arrayZ[i];
     }
+    length += other.length;
   }
 
   void
@@ -432,6 +466,15 @@ struct hb_vector_t
 
     return true;
   }
+  bool alloc_exact (unsigned int size)
+  {
+    return alloc (size, true);
+  }
+
+  void clear ()
+  {
+    resize (0);
+  }
 
   bool resize (int size_, bool initialize = true, bool exact = false)
   {
@@ -497,7 +540,7 @@ struct hb_vector_t
     shrink_vector (size);
 
     if (shrink_memory)
-      alloc (size, true); /* To force shrinking memory if needed. */
+      alloc_exact (size); /* To force shrinking memory if needed. */
   }
 
 
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/hb.hh b/source/libs/harfbuzz/harfbuzz-src/src/hb.hh
index fe466fe1f..7c265483f 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/hb.hh
+++ b/source/libs/harfbuzz/harfbuzz-src/src/hb.hh
@@ -131,6 +131,7 @@
 #pragma GCC diagnostic ignored "-Wclass-memaccess"
 #pragma GCC diagnostic ignored "-Wcast-function-type-strict" // https://github.com/harfbuzz/harfbuzz/pull/3859#issuecomment-1295409126
 #pragma GCC diagnostic ignored "-Wdangling-reference" // https://github.com/harfbuzz/harfbuzz/issues/4043
+#pragma GCC diagnostic ignored "-Wdangling-pointer" // Trigerred by hb_decycler_node_t().
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #pragma GCC diagnostic ignored "-Wformat-zero-length"
 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
@@ -281,7 +282,9 @@ extern "C" void  hb_free_impl(void *ptr);
 #define __attribute__(x)
 #endif
 
-#if defined(__GNUC__) && (__GNUC__ >= 3)
+#if defined(__MINGW32__) && (__GNUC__ >= 3)
+#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (gnu_printf, format_idx, arg_idx)))
+#elif defined(__GNUC__) && (__GNUC__ >= 3)
 #define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx)))
 #else
 #define HB_PRINTF_FUNC(format_idx, arg_idx)
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/meson.build b/source/libs/harfbuzz/harfbuzz-src/src/meson.build
index b9daabf01..8e40e0498 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/meson.build
+++ b/source/libs/harfbuzz/harfbuzz-src/src/meson.build
@@ -43,6 +43,7 @@ hb_base_sources = files(
   'hb-common.cc',
   'hb-config.hh',
   'hb-debug.hh',
+  'hb-decycler.hh',
   'hb-dispatch.hh',
   'hb-draw.cc',
   'hb-draw.hh',
@@ -725,6 +726,7 @@ if get_option('tests').enabled()
     'test-bimap': ['test-bimap.cc', 'hb-static.cc'],
     'test-cff': ['test-cff.cc', 'hb-static.cc'],
     'test-classdef-graph': ['graph/test-classdef-graph.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'],
+    'test-decycler': ['test-decycler.cc', 'hb-static.cc'],
     'test-iter': ['test-iter.cc', 'hb-static.cc'],
     'test-machinery': ['test-machinery.cc', 'hb-static.cc'],
     'test-map': ['test-map.cc', 'hb-static.cc'],
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/test-tuple-varstore.cc b/source/libs/harfbuzz/harfbuzz-src/src/test-tuple-varstore.cc
index 1e07d27d3..9fdbacafa 100644
--- a/source/libs/harfbuzz/harfbuzz-src/src/test-tuple-varstore.cc
+++ b/source/libs/harfbuzz/harfbuzz-src/src/test-tuple-varstore.cc
@@ -38,15 +38,15 @@ test_decompile_cvar ()
   hb_map_t axis_idx_tag_map;
   axis_idx_tag_map.set (0, axis_tag);
 
-  OT::TupleVariationData::tuple_variations_t tuple_variations;
+  OT::TupleVariationData<>::tuple_variations_t tuple_variations;
   hb_vector_t<unsigned> shared_indices;
-  OT::TupleVariationData::tuple_iterator_t iterator;
+  OT::TupleVariationData<>::tuple_iterator_t iterator;
 
-  const OT::TupleVariationData* tuple_var_data = reinterpret_cast<const OT::TupleVariationData*> (cvar_data + 4);
+  const OT::TupleVariationData<>* tuple_var_data = reinterpret_cast<const OT::TupleVariationData<>*> (cvar_data + 4);
 
   unsigned len = sizeof (cvar_data);
   hb_bytes_t var_data_bytes{(const char* ) cvar_data + 4, len - 4};
-  bool result = OT::TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, cvar_table,
+  bool result = OT::TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, cvar_table,
                                                             shared_indices, &iterator);
   assert (result);
 
@@ -66,7 +66,7 @@ test_decompile_cvar ()
   assert (tuple_variations.tuple_vars[0].axis_tuples.get (axis_tag) == Triple (-1.0, -1.0, 0.0));
   assert (tuple_variations.tuple_vars[1].axis_tuples.get (axis_tag) == Triple (0.0, 1.0, 1.0));
 
-  hb_vector_t<double> deltas_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, -1.0, 0.0, -3.0, 1.0, 0.0, -1.0, 0.0, -3.0, 1.0, 0.0, -37.0, -37.0, -26.0, -26.0, 0.0, 0.0, 0.0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0, -3.0, 0.0, 2.0, -29.0, -29.0, -20.0, -20.0, 0.0, 0.0, 0.0, 1.0, -29.0, -29.0, -20.0, -20.0, 0.0, 0.0, 0.0, 1.0};
+  hb_vector_t<float> deltas_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, -1.0, 0.0, -3.0, 1.0, 0.0, -1.0, 0.0, -3.0, 1.0, 0.0, -37.0, -37.0, -26.0, -26.0, 0.0, 0.0, 0.0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0, -3.0, 0.0, 2.0, -29.0, -29.0, -20.0, -20.0, 0.0, 0.0, 0.0, 1.0, -29.0, -29.0, -20.0, -20.0, 0.0, 0.0, 0.0, 1.0};
   for (unsigned i = 0; i < 65; i++)
   {
     if (i < 23)
@@ -78,7 +78,7 @@ test_decompile_cvar ()
     }
   }
 
-  hb_vector_t<double> deltas_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, 1.0, 0.0, 5.0, -3.0, 0.0, 1.0, 0.0, 5.0, -3.0, 0.0, 97.0, 97.0, 68.0, 68.0, 0.0, 0.0, 0.0, 5.0, 0.0, 0.0, 1.0, -1.0, 1.0, 7.0, -1.0, -5.0, 73.0, 73.0, 53.0, 53.0, 0.0, 0.0, 0.0, -1.0, 73.0, 73.0, 53.0, 53.0, 0.0, 0.0, 0.0, -1.0};
+  hb_vector_t<float> deltas_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, 1.0, 0.0, 5.0, -3.0, 0.0, 1.0, 0.0, 5.0, -3.0, 0.0, 97.0, 97.0, 68.0, 68.0, 0.0, 0.0, 0.0, 5.0, 0.0, 0.0, 1.0, -1.0, 1.0, 7.0, -1.0, -5.0, 73.0, 73.0, 53.0, 53.0, 0.0, 0.0, 0.0, -1.0, 73.0, 73.0, 53.0, 53.0, 0.0, 0.0, 0.0, -1.0};
   for (unsigned i = 0 ; i < 65; i++)
   {
     if (i < 23)
@@ -106,9 +106,9 @@ test_decompile_cvar ()
   assert (tuple_variations.tuple_vars[0].axis_tuples.get (axis_tag) == Triple (-1.0, -1.0, 0.0));
   assert (tuple_variations.tuple_vars[1].axis_tuples.get (axis_tag) == Triple (0.0, 1.0, 1.0));
 
-  hb_vector_t<double> rounded_deltas_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, -1, 0.0, -2, 1, 0.0, -1, 0.0, -2, 1, 0.0, -19, -19, -13, -13, 0.0, 0.0, 0.0, -2, 0.0, 0.0, 0.0, 0.0, 0.0, -2, 0.0, 1, -15, -15, -10.0, -10.0, 0.0, 0.0, 0.0, 1, -15, -15, -10.0, -10.0, 0.0, 0.0, 0.0, 1};
+  hb_vector_t<float> rounded_deltas_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, -1, 0.0, -2, 1, 0.0, -1, 0.0, -2, 1, 0.0, -19, -19, -13, -13, 0.0, 0.0, 0.0, -2, 0.0, 0.0, 0.0, 0.0, 0.0, -2, 0.0, 1, -15, -15, -10.0, -10.0, 0.0, 0.0, 0.0, 1, -15, -15, -10.0, -10.0, 0.0, 0.0, 0.0, 1};
 
-  hb_vector_t<double> rounded_deltas_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, 1, 0.0, 4, -2, 0.0, 1, 0.0, 4, -2, 0.0, 68, 68, 48, 48, 0.0, 0.0, 0.0, 4, 0.0, 0.0, 1, -1, 1, 5, -1, -4, 51, 51, 37, 37, 0.0, 0.0, 0.0, -1, 51, 51, 37, 37, 0.0, 0.0, 0.0, -1};
+  hb_vector_t<float> rounded_deltas_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, 1, 0.0, 4, -2, 0.0, 1, 0.0, 4, -2, 0.0, 68, 68, 48, 48, 0.0, 0.0, 0.0, 4, 0.0, 0.0, 1, -1, 1, 5, -1, -4, 51, 51, 37, 37, 0.0, 0.0, 0.0, -1, 51, 51, 37, 37, 0.0, 0.0, 0.0, -1};
 
   for (unsigned i = 0; i < 65; i++)
   {
diff --git a/source/libs/harfbuzz/version.ac b/source/libs/harfbuzz/version.ac
index 27b278353..c52799ab6 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], [10.2.0])
+m4_define([harfbuzz_version], [10.4.0])
diff --git a/source/libs/libpng/ChangeLog b/source/libs/libpng/ChangeLog
index 949797268..e43efc31a 100644
--- a/source/libs/libpng/ChangeLog
+++ b/source/libs/libpng/ChangeLog
@@ -1,3 +1,8 @@
+2025-03-09 Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	Import libpng-1.6.47.
+	* version.ac: Adjust.
+
 2025-01-25 Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
 
 	Import libpng-1.6.46.
diff --git a/source/libs/libpng/README b/source/libs/libpng/README
index 22781cdc7..92e4f1aed 100644
--- a/source/libs/libpng/README
+++ b/source/libs/libpng/README
@@ -1,4 +1,4 @@
-	Building libpng-1.6.45 as part of the TL tree
+	Building libpng-1.6.47 as part of the TL tree
 	=============================================
 
 This directory libs/libpng/ uses a proxy Makefile.am to build the libpng library
@@ -14,4 +14,4 @@ copied to libs/libpng/configure.ac.
 =============================
 
 2009-07-23	Peter Breitenlohner <peb@mppmu.mpg.de>
-2025-01-25	Akira Kakuto <kakuto@jcom.zaq.ne.jp>
+2025-03-09	Akira Kakuto <kakuto@jcom.zaq.ne.jp>
diff --git a/source/libs/libpng/TLpatches/ChangeLog b/source/libs/libpng/TLpatches/ChangeLog
index efd1df78f..b189fe224 100644
--- a/source/libs/libpng/TLpatches/ChangeLog
+++ b/source/libs/libpng/TLpatches/ChangeLog
@@ -1,3 +1,7 @@
+2025-03-09  Akira Kakuto <kakuto@jcom.zaq.ne.jp>
+
+	Import libpng-1.6.47.
+
 2025-01-25  Akira Kakuto <kakuto@jcom.zaq.ne.jp>
 
 	Import libpng-1.6.46.
diff --git a/source/libs/libpng/TLpatches/TL-Changes b/source/libs/libpng/TLpatches/TL-Changes
index 2a3cfe306..938aed4ac 100644
--- a/source/libs/libpng/TLpatches/TL-Changes
+++ b/source/libs/libpng/TLpatches/TL-Changes
@@ -1,5 +1,5 @@
-Changes applied to the libpng-1.6.46/ tree as obtained from:
-	https://sourceforge.net/projects/libpng/files/libpng16/1.6.46/
+Changes applied to the libpng-1.6.47/ tree as obtained from:
+	https://sourceforge.net/projects/libpng/files/libpng16/1.6.47/
 
 Copied:
 	scripts/pnglibconf.h.prebuilt -> pnglibconf.h
diff --git a/source/libs/libpng/configure b/source/libs/libpng/configure
index 73550681c..88ce80b06 100755
--- a/source/libs/libpng/configure
+++ b/source/libs/libpng/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for libpng (TeX Live) 1.6.46.
+# Generated by GNU Autoconf 2.72 for libpng (TeX Live) 1.6.47.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -604,8 +604,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='libpng (TeX Live)'
 PACKAGE_TARNAME='libpng--tex-live-'
-PACKAGE_VERSION='1.6.46'
-PACKAGE_STRING='libpng (TeX Live) 1.6.46'
+PACKAGE_VERSION='1.6.47'
+PACKAGE_STRING='libpng (TeX Live) 1.6.47'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1342,7 +1342,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 libpng (TeX Live) 1.6.46 to adapt to many kinds of systems.
+'configure' configures libpng (TeX Live) 1.6.47 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1414,7 +1414,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of libpng (TeX Live) 1.6.46:";;
+     short | recursive ) echo "Configuration of libpng (TeX Live) 1.6.47:";;
    esac
   cat <<\_ACEOF
 
@@ -1534,7 +1534,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-libpng (TeX Live) configure 1.6.46
+libpng (TeX Live) configure 1.6.47
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -1861,7 +1861,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 libpng (TeX Live) $as_me 1.6.46, which was
+It was created by libpng (TeX Live) $as_me 1.6.47, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -4822,7 +4822,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='libpng--tex-live-'
- VERSION='1.6.46'
+ VERSION='1.6.47'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -7996,7 +7996,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 libpng (TeX Live) $as_me 1.6.46, which was
+This file was extended by libpng (TeX Live) $as_me 1.6.47, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -8064,7 +8064,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="\\
-libpng (TeX Live) config.status 1.6.46
+libpng (TeX Live) config.status 1.6.47
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/libs/libpng/libpng-src/ANNOUNCE b/source/libs/libpng/libpng-src/ANNOUNCE
index 579824fbe..603b2df48 100644
--- a/source/libs/libpng/libpng-src/ANNOUNCE
+++ b/source/libs/libpng/libpng-src/ANNOUNCE
@@ -1,5 +1,5 @@
-libpng 1.6.46 - January 23, 2025
-================================
+libpng 1.6.47 - February 18, 2025
+=================================
 
 This is a public release of libpng, intended for use in production code.
 
@@ -9,13 +9,13 @@ Files available for download
 
 Source files with LF line endings (for Unix/Linux):
 
- * libpng-1.6.46.tar.xz (LZMA-compressed, recommended)
- * libpng-1.6.46.tar.gz (deflate-compressed)
+ * libpng-1.6.47.tar.xz (LZMA-compressed, recommended)
+ * libpng-1.6.47.tar.gz (deflate-compressed)
 
 Source files with CRLF line endings (for Windows):
 
- * lpng1646.7z (LZMA-compressed, recommended)
- * lpng1646.zip (deflate-compressed)
+ * lpng1647.7z (LZMA-compressed, recommended)
+ * lpng1647.zip (deflate-compressed)
 
 Other information:
 
@@ -25,17 +25,19 @@ Other information:
  * TRADEMARK.md
 
 
-Changes from version 1.6.45 to version 1.6.46
+Changes from version 1.6.46 to version 1.6.47
 ---------------------------------------------
 
- * Added support for the mDCV and cLLI chunks.
+ * Modified the behaviour of colorspace chunks in order to adhere
+   to the new precedence rules formulated in the latest draft of
+   the PNG Specification.
    (Contributed by John Bowler)
- * Fixed a build issue affecting C89 compilers.
-   This was a regression introduced in libpng-1.6.45.
-   (Contributed by John Bowler)
- * Added makefile.c89, specifically for testing C89 compilers.
- * Cleaned up contrib/pngminus: corrected an old typo, removed an old
-   workaround, and updated the CMake file.
+ * Fixed a latent bug in `png_write_iCCP`.
+   This would have been a read-beyond-end-of-malloc vulnerability,
+   introduced early in the libpng-1.6.0 development, yet (fortunately!)
+   it was inaccessible before the above-mentioned modification of the
+   colorspace precedence rules, due to pre-existing colorspace checks.
+   (Reported by Bob Friesenhahn; fixed by John Bowler)
 
 
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
diff --git a/source/libs/libpng/libpng-src/CHANGES b/source/libs/libpng/libpng-src/CHANGES
index e6aa1db13..834b5e192 100644
--- a/source/libs/libpng/libpng-src/CHANGES
+++ b/source/libs/libpng/libpng-src/CHANGES
@@ -6239,6 +6239,18 @@ Version 1.6.46 [January 23, 2025]
   Cleaned up contrib/pngminus: corrected an old typo, removed an old
     workaround, and updated the CMake file.
 
+Version 1.6.47 [February 18, 2025]
+  Modified the behaviour of colorspace chunks in order to adhere
+    to the new precedence rules formulated in the latest draft of
+    the PNG Specification.
+    (Contributed by John Bowler)
+  Fixed a latent bug in `png_write_iCCP`.
+    This would have been a read-beyond-end-of-malloc vulnerability,
+    introduced early in the libpng-1.6.0 development, yet (fortunately!)
+    it was inaccessible before the above-mentioned modification of the
+    colorspace precedence rules, due to pre-existing colorspace checks.
+    (Reported by Bob Friesenhahn; fixed by John Bowler)
+
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
 Subscription is required; visit
 https://lists.sourceforge.net/lists/listinfo/png-mng-implement
diff --git a/source/libs/libpng/libpng-src/CMakeLists.txt b/source/libs/libpng/libpng-src/CMakeLists.txt
index 99d028a13..4a97bd50e 100644
--- a/source/libs/libpng/libpng-src/CMakeLists.txt
+++ b/source/libs/libpng/libpng-src/CMakeLists.txt
@@ -18,7 +18,7 @@ cmake_minimum_required(VERSION 3.14)
 
 set(PNGLIB_MAJOR 1)
 set(PNGLIB_MINOR 6)
-set(PNGLIB_REVISION 46)
+set(PNGLIB_REVISION 47)
 set(PNGLIB_SUBREVISION 0)
 #set(PNGLIB_SUBREVISION "git")
 set(PNGLIB_VERSION ${PNGLIB_MAJOR}.${PNGLIB_MINOR}.${PNGLIB_REVISION})
diff --git a/source/libs/libpng/libpng-src/README b/source/libs/libpng/libpng-src/README
index 3f3f02023..57952fb21 100644
--- a/source/libs/libpng/libpng-src/README
+++ b/source/libs/libpng/libpng-src/README
@@ -1,4 +1,4 @@
-README for libpng version 1.6.46
+README for libpng version 1.6.47
 ================================
 
 See the note about version numbers near the top of `png.h`.
diff --git a/source/libs/libpng/libpng-src/ci/ci_verify_cmake.sh b/source/libs/libpng/libpng-src/ci/ci_verify_cmake.sh
index d48062cf5..3e05ec309 100644
--- a/source/libs/libpng/libpng-src/ci/ci_verify_cmake.sh
+++ b/source/libs/libpng/libpng-src/ci/ci_verify_cmake.sh
@@ -64,6 +64,7 @@ function ci_trace_build {
     ci_info "environment option: \$CI_RANLIB: '$CI_RANLIB'"
     ci_info "environment option: \$CI_SANITIZERS: '$CI_SANITIZERS'"
     ci_info "environment option: \$CI_FORCE: '$CI_FORCE'"
+    ci_info "environment option: \$CI_NO_BUILD: '$CI_NO_BUILD'"
     ci_info "environment option: \$CI_NO_TEST: '$CI_NO_TEST'"
     ci_info "environment option: \$CI_NO_INSTALL: '$CI_NO_INSTALL'"
     ci_info "environment option: \$CI_NO_CLEAN: '$CI_NO_CLEAN'"
@@ -148,10 +149,12 @@ function ci_build {
                          -S . \
                          -DCMAKE_INSTALL_PREFIX="$CI_INSTALL_DIR" \
                          "${all_cmake_vars[@]}"
-    # Spawn "cmake --build ...".
-    ci_spawn "$CI_CMAKE" --build "$CI_BUILD_DIR" \
-                         --config "$CI_CMAKE_BUILD_TYPE" \
-                         "${all_cmake_build_flags[@]}"
+    ci_expr $((CI_NO_BUILD)) || {
+        # Spawn "cmake --build ...".
+        ci_spawn "$CI_CMAKE" --build "$CI_BUILD_DIR" \
+                             --config "$CI_CMAKE_BUILD_TYPE" \
+                             "${all_cmake_build_flags[@]}"
+    }
     ci_expr $((CI_NO_TEST)) || {
         # Spawn "ctest" if testing is not disabled.
         ci_spawn pushd "$CI_BUILD_DIR"
diff --git a/source/libs/libpng/libpng-src/ci/ci_verify_configure.sh b/source/libs/libpng/libpng-src/ci/ci_verify_configure.sh
index b0e4d4944..9c3a28809 100644
--- a/source/libs/libpng/libpng-src/ci/ci_verify_configure.sh
+++ b/source/libs/libpng/libpng-src/ci/ci_verify_configure.sh
@@ -58,6 +58,7 @@ function ci_trace_build {
     ci_info "environment option: \$CI_LD_FLAGS: '$CI_LD_FLAGS'"
     ci_info "environment option: \$CI_SANITIZERS: '$CI_SANITIZERS'"
     ci_info "environment option: \$CI_FORCE: '$CI_FORCE'"
+    ci_info "environment option: \$CI_NO_BUILD: '$CI_NO_BUILD'"
     ci_info "environment option: \$CI_NO_TEST: '$CI_NO_TEST'"
     ci_info "environment option: \$CI_NO_INSTALL: '$CI_NO_INSTALL'"
     ci_info "environment option: \$CI_NO_CLEAN: '$CI_NO_CLEAN'"
@@ -131,8 +132,10 @@ function ci_build {
     ci_spawn cd "$CI_BUILD_DIR"
     # Spawn "configure".
     ci_spawn "$CI_SRC_DIR/configure" --prefix="$CI_INSTALL_DIR" $CI_CONFIGURE_FLAGS
-    # Spawn "make".
-    ci_spawn "$CI_MAKE" $CI_MAKE_FLAGS
+    ci_expr $((CI_NO_BUILD)) || {
+        # Spawn "make".
+        ci_spawn "$CI_MAKE" $CI_MAKE_FLAGS
+    }
     ci_expr $((CI_NO_TEST)) || {
         # Spawn "make test" if testing is not disabled.
         ci_spawn "$CI_MAKE" $CI_MAKE_FLAGS test
diff --git a/source/libs/libpng/libpng-src/ci/ci_verify_makefiles.sh b/source/libs/libpng/libpng-src/ci/ci_verify_makefiles.sh
index e0681b4d8..2d3ec72ec 100644
--- a/source/libs/libpng/libpng-src/ci/ci_verify_makefiles.sh
+++ b/source/libs/libpng/libpng-src/ci/ci_verify_makefiles.sh
@@ -51,6 +51,7 @@ function ci_trace_build {
     ci_info "environment option: \$CI_LIBS: '$CI_LIBS'"
     ci_info "environment option: \$CI_SANITIZERS: '$CI_SANITIZERS'"
     ci_info "environment option: \$CI_FORCE: '$CI_FORCE'"
+    ci_info "environment option: \$CI_NO_BUILD: '$CI_NO_BUILD'"
     ci_info "environment option: \$CI_NO_TEST: '$CI_NO_TEST'"
     ci_info "environment option: \$CI_NO_CLEAN: '$CI_NO_CLEAN'"
     ci_info "executable: \$CI_MAKE: $(command -V "$CI_MAKE")"
@@ -145,10 +146,12 @@ function ci_build {
     for my_makefile in $CI_MAKEFILES
     do
         ci_info "using makefile: $my_makefile"
-        # Spawn "make".
-        ci_spawn "$CI_MAKE" -f "$my_makefile" \
-                            "${all_make_flags[@]}" \
-                            "${all_make_vars[@]}"
+        ci_expr $((CI_NO_BUILD)) || {
+            # Spawn "make".
+            ci_spawn "$CI_MAKE" -f "$my_makefile" \
+                                "${all_make_flags[@]}" \
+                                "${all_make_vars[@]}"
+        }
         ci_expr $((CI_NO_TEST)) || {
             # Spawn "make test" if testing is not disabled.
             ci_spawn "$CI_MAKE" -f "$my_makefile" \
diff --git a/source/libs/libpng/libpng-src/ci/lib/ci.lib.sh b/source/libs/libpng/libpng-src/ci/lib/ci.lib.sh
index 03e866b5c..692851fc0 100644
--- a/source/libs/libpng/libpng-src/ci/lib/ci.lib.sh
+++ b/source/libs/libpng/libpng-src/ci/lib/ci.lib.sh
@@ -91,6 +91,9 @@ function ci_spawn {
 [[ ${CI_FORCE:-0} == [01] ]] || {
     ci_err "bad boolean option: \$CI_FORCE: '$CI_FORCE'"
 }
+[[ ${CI_NO_BUILD:-0} == [01] ]] || {
+    ci_err "bad boolean option: \$CI_NO_BUILD: '$CI_NO_BUILD'"
+}
 [[ ${CI_NO_TEST:-0} == [01] ]] || {
     ci_err "bad boolean option: \$CI_NO_TEST: '$CI_NO_TEST'"
 }
@@ -100,3 +103,9 @@ function ci_spawn {
 [[ ${CI_NO_CLEAN:-0} == [01] ]] || {
     ci_err "bad boolean option: \$CI_NO_CLEAN: '$CI_NO_CLEAN'"
 }
+if ci_expr $((CI_NO_BUILD))
+then
+    ci_expr $((CI_NO_TEST && CI_NO_INSTALL)) || {
+        ci_err "\$CI_NO_BUILD requires \$CI_NO_TEST and \$CI_NO_INSTALL"
+    }
+fi
diff --git a/source/libs/libpng/libpng-src/configure.ac b/source/libs/libpng/libpng-src/configure.ac
index 735b55a95..df48325e0 100644
--- a/source/libs/libpng/libpng-src/configure.ac
+++ b/source/libs/libpng/libpng-src/configure.ac
@@ -25,7 +25,7 @@ AC_PREREQ([2.68])
 
 dnl Version number stuff here:
 
-AC_INIT([libpng],[1.6.46],[png-mng-implement@lists.sourceforge.net])
+AC_INIT([libpng],[1.6.47],[png-mng-implement@lists.sourceforge.net])
 AC_CONFIG_MACRO_DIR([scripts/autoconf])
 
 # libpng does not follow GNU file name conventions (hence 'foreign')
@@ -46,10 +46,10 @@ dnl automake, so the following is not necessary (and is not defined anyway):
 dnl AM_PREREQ([1.11.2])
 dnl stop configure from automagically running automake
 
-PNGLIB_VERSION=1.6.46
+PNGLIB_VERSION=1.6.47
 PNGLIB_MAJOR=1
 PNGLIB_MINOR=6
-PNGLIB_RELEASE=46
+PNGLIB_RELEASE=47
 
 dnl End of version number stuff
 
diff --git a/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c b/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c
index d22f7bcd0..2e2dd0894 100644
--- a/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c
+++ b/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c
@@ -542,6 +542,7 @@ typedef enum
 struct display
 {
    jmp_buf        error_return;      /* Where to go to on error */
+   error_level    error_code;        /* Set before longjmp */
 
    const char    *filename;          /* The name of the original file */
    const char    *operation;         /* Operation being performed */
@@ -762,7 +763,10 @@ display_log(struct display *dp, error_level level, const char *fmt, ...)
 
    /* Errors cause this routine to exit to the fail code */
    if (level > APP_FAIL || (level > ERRORS && !(dp->options & CONTINUE)))
+   {
+      dp->error_code = level;
       longjmp(dp->error_return, level);
+    }
 }
 
 /* error handler callbacks for libpng */
@@ -1570,18 +1574,19 @@ static int
 do_test(struct display *dp, const char *file)
    /* Exists solely to isolate the setjmp clobbers */
 {
-   int ret = setjmp(dp->error_return);
+   dp->error_code = VERBOSE; /* The "lowest" level */
 
-   if (ret == 0)
+   if (setjmp(dp->error_return) == 0)
    {
       test_one_file(dp, file);
       return 0;
    }
 
-   else if (ret < ERRORS) /* shouldn't longjmp on warnings */
-      display_log(dp, INTERNAL_ERROR, "unexpected return code %d", ret);
+   else if (dp->error_code < ERRORS) /* shouldn't longjmp on warnings */
+      display_log(dp, INTERNAL_ERROR, "unexpected return code %d",
+                  dp->error_code);
 
-   return ret;
+   return dp->error_code;
 }
 
 int
@@ -1681,7 +1686,11 @@ main(int argc, char **argv)
             int ret = do_test(&d, argv[i]);
 
             if (ret > QUIET) /* abort on user or internal error */
+            {
+               display_clean(&d);
+               display_destroy(&d);
                return 99;
+            }
          }
 
          /* Here on any return, including failures, except user/internal issues
diff --git a/source/libs/libpng/libpng-src/libpng-manual.txt b/source/libs/libpng/libpng-src/libpng-manual.txt
index e81e477c1..862fe2c5d 100644
--- a/source/libs/libpng/libpng-src/libpng-manual.txt
+++ b/source/libs/libpng/libpng-src/libpng-manual.txt
@@ -9,7 +9,7 @@ libpng-manual.txt - A description on how to use and modify libpng
 
  Based on:
 
- libpng version 1.6.36, December 2018, through 1.6.46 - January 2025
+ libpng version 1.6.36, December 2018, through 1.6.47 - February 2025
  Updated and distributed by Cosmin Truta
  Copyright (c) 2018-2025 Cosmin Truta
 
diff --git a/source/libs/libpng/libpng-src/libpng.3 b/source/libs/libpng/libpng-src/libpng.3
index a23193be1..923b6772e 100644
--- a/source/libs/libpng/libpng-src/libpng.3
+++ b/source/libs/libpng/libpng-src/libpng.3
@@ -1,6 +1,6 @@
-.TH LIBPNG 3 "January 23, 2025"
+.TH LIBPNG 3 "February 18, 2025"
 .SH NAME
-libpng \- Portable Network Graphics (PNG) Reference Library 1.6.46
+libpng \- Portable Network Graphics (PNG) Reference Library 1.6.47
 
 .SH SYNOPSIS
 \fB#include <png.h>\fP
@@ -528,7 +528,7 @@ libpng-manual.txt - A description on how to use and modify libpng
 
  Based on:
 
- libpng version 1.6.36, December 2018, through 1.6.46 - January 2025
+ libpng version 1.6.36, December 2018, through 1.6.47 - February 2025
  Updated and distributed by Cosmin Truta
  Copyright (c) 2018-2025 Cosmin Truta
 
diff --git a/source/libs/libpng/libpng-src/libpngpf.3 b/source/libs/libpng/libpng-src/libpngpf.3
index d5262c4b0..9c4dda2a6 100644
--- a/source/libs/libpng/libpng-src/libpngpf.3
+++ b/source/libs/libpng/libpng-src/libpngpf.3
@@ -1,6 +1,6 @@
-.TH LIBPNGPF 3 "January 23, 2025"
+.TH LIBPNGPF 3 "February 18, 2025"
 .SH NAME
-libpng \- Portable Network Graphics (PNG) Reference Library 1.6.46
+libpng \- Portable Network Graphics (PNG) Reference Library 1.6.47
 (private functions)
 
 .SH SYNOPSIS
diff --git a/source/libs/libpng/libpng-src/png.5 b/source/libs/libpng/libpng-src/png.5
index 16c1032fb..ee4a2b20d 100644
--- a/source/libs/libpng/libpng-src/png.5
+++ b/source/libs/libpng/libpng-src/png.5
@@ -1,4 +1,4 @@
-.TH PNG 5 "January 23, 2025"
+.TH PNG 5 "February 18, 2025"
 .SH NAME
 png \- Portable Network Graphics (PNG) format
 
@@ -20,10 +20,10 @@ matching on heterogeneous platforms.
 .SH "SEE ALSO"
 .BR "libpng"(3), " zlib"(3), " deflate"(5), " " and " zlib"(5)
 .LP
-PNG Specification (Third Edition) Candidate Recommendation Draft, July 2024:
+PNG Specification (Third Edition) Candidate Recommendation Draft, January 2025:
 .IP
 .br
-https://www.w3.org/TR/2024/CRD-png-3-20240718/
+https://www.w3.org/TR/2025/CRD-png-3-20250121/
 .LP
 PNG Specification (Second Edition), November 2003:
 .IP
diff --git a/source/libs/libpng/libpng-src/png.c b/source/libs/libpng/libpng-src/png.c
index cbd54e1af..6d533ec40 100644
--- a/source/libs/libpng/libpng-src/png.c
+++ b/source/libs/libpng/libpng-src/png.c
@@ -13,7 +13,34 @@
 #include "pngpriv.h"
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_46 Your_png_h_is_not_version_1_6_46;
+typedef png_libpng_version_1_6_47 Your_png_h_is_not_version_1_6_47;
+
+/* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
+ * corresponding macro definitions.  This causes a compile time failure if
+ * something is wrong but generates no code.
+ *
+ * (1) The first check is that the PNG_CHUNK(cHNK, index) 'index' values must
+ * increment from 0 to the last value.
+ */
+#define PNG_CHUNK(cHNK, index) != (index) || ((index)+1)
+
+#if 0 PNG_KNOWN_CHUNKS < 0
+#  error PNG_KNOWN_CHUNKS chunk definitions are not in order
+#endif
+
+#undef PNG_CHUNK
+
+/* (2) The chunk name macros, png_cHNK, must all be valid and defined.  Since
+ * this is a preprocessor test undefined pp-tokens come out as zero and will
+ * fail this test.
+ */
+#define PNG_CHUNK(cHNK, index) !PNG_CHUNK_NAME_VALID(png_ ## cHNK) ||
+
+#if PNG_KNOWN_CHUNKS 0
+#  error png_cHNK not defined for some known cHNK
+#endif
+
+#undef PNG_CHUNK
 
 /* Tells libpng that we have already handled the first "num_bytes" bytes
  * of the PNG file signature.  If the PNG data is embedded into another
@@ -241,21 +268,23 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
     */
    memset(&create_struct, 0, (sizeof create_struct));
 
-   /* Added at libpng-1.2.6 */
 #  ifdef PNG_USER_LIMITS_SUPPORTED
       create_struct.user_width_max = PNG_USER_WIDTH_MAX;
       create_struct.user_height_max = PNG_USER_HEIGHT_MAX;
 
 #     ifdef PNG_USER_CHUNK_CACHE_MAX
-      /* Added at libpng-1.2.43 and 1.4.0 */
       create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX;
 #     endif
 
-#     ifdef PNG_USER_CHUNK_MALLOC_MAX
-      /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists
-       * in png_struct regardless.
-       */
+#     if PNG_USER_CHUNK_MALLOC_MAX > 0 /* default to compile-time limit */
       create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX;
+
+      /* No compile-time limit, so initialize to the system limit: */
+#     elif defined PNG_MAX_MALLOC_64K /* legacy system limit */
+      create_struct.user_chunk_malloc_max = 65536U;
+
+#     else /* modern system limit SIZE_MAX (C99) */
+      create_struct.user_chunk_malloc_max = PNG_SIZE_MAX;
 #     endif
 #  endif
 
@@ -597,13 +626,6 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,
    /* Free any eXIf entry */
    if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0)
    {
-# ifdef PNG_READ_eXIf_SUPPORTED
-      if (info_ptr->eXIf_buf)
-      {
-         png_free(png_ptr, info_ptr->eXIf_buf);
-         info_ptr->eXIf_buf = NULL;
-      }
-# endif
       if (info_ptr->exif)
       {
          png_free(png_ptr, info_ptr->exif);
@@ -793,7 +815,7 @@ png_get_copyright(png_const_structrp png_ptr)
    return PNG_STRING_COPYRIGHT
 #else
    return PNG_STRING_NEWLINE \
-      "libpng version 1.6.46" PNG_STRING_NEWLINE \
+      "libpng version 1.6.47" PNG_STRING_NEWLINE \
       "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \
       "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
       PNG_STRING_NEWLINE \
@@ -1038,169 +1060,6 @@ png_zstream_error(png_structrp png_ptr, int ret)
    }
 }
 
-/* png_convert_size: a PNGAPI but no longer in png.h, so deleted
- * at libpng 1.5.5!
- */
-
-/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */
-#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */
-static int
-png_colorspace_check_gamma(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_fixed_point gAMA, int from)
-   /* This is called to check a new gamma value against an existing one.  The
-    * routine returns false if the new gamma value should not be written.
-    *
-    * 'from' says where the new gamma value comes from:
-    *
-    *    0: the new gamma value is the libpng estimate for an ICC profile
-    *    1: the new gamma value comes from a gAMA chunk
-    *    2: the new gamma value comes from an sRGB chunk
-    */
-{
-   png_fixed_point gtest;
-
-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-       (png_muldiv(&gtest, colorspace->gamma, PNG_FP_1, gAMA) == 0  ||
-      png_gamma_significant(gtest) != 0))
-   {
-      /* Either this is an sRGB image, in which case the calculated gamma
-       * approximation should match, or this is an image with a profile and the
-       * value libpng calculates for the gamma of the profile does not match the
-       * value recorded in the file.  The former, sRGB, case is an error, the
-       * latter is just a warning.
-       */
-      if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2)
-      {
-         png_chunk_report(png_ptr, "gamma value does not match sRGB",
-             PNG_CHUNK_ERROR);
-         /* Do not overwrite an sRGB value */
-         return from == 2;
-      }
-
-      else /* sRGB tag not involved */
-      {
-         png_chunk_report(png_ptr, "gamma value does not match libpng estimate",
-             PNG_CHUNK_WARNING);
-         return from == 1;
-      }
-   }
-
-   return 1;
-}
-
-void /* PRIVATE */
-png_colorspace_set_gamma(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_fixed_point gAMA)
-{
-   /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't
-    * occur.  Since the fixed point representation is asymmetrical it is
-    * possible for 1/gamma to overflow the limit of 21474 and this means the
-    * gamma value must be at least 5/100000 and hence at most 20000.0.  For
-    * safety the limits here are a little narrower.  The values are 0.00016 to
-    * 6250.0, which are truly ridiculous gamma values (and will produce
-    * displays that are all black or all white.)
-    *
-    * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk
-    * handling code, which only required the value to be >0.
-    */
-   png_const_charp errmsg;
-
-   if (gAMA < 16 || gAMA > 625000000)
-      errmsg = "gamma value out of range";
-
-#  ifdef PNG_READ_gAMA_SUPPORTED
-   /* Allow the application to set the gamma value more than once */
-   else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
-      (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0)
-      errmsg = "duplicate";
-#  endif
-
-   /* Do nothing if the colorspace is already invalid */
-   else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return;
-
-   else
-   {
-      if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA,
-          1/*from gAMA*/) != 0)
-      {
-         /* Store this gamma value. */
-         colorspace->gamma = gAMA;
-         colorspace->flags |=
-            (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA);
-      }
-
-      /* At present if the check_gamma test fails the gamma of the colorspace is
-       * not updated however the colorspace is not invalidated.  This
-       * corresponds to the case where the existing gamma comes from an sRGB
-       * chunk or profile.  An error message has already been output.
-       */
-      return;
-   }
-
-   /* Error exit - errmsg has been set. */
-   colorspace->flags |= PNG_COLORSPACE_INVALID;
-   png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR);
-}
-
-void /* PRIVATE */
-png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr)
-{
-   if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-   {
-      /* Everything is invalid */
-      info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB|
-         PNG_INFO_iCCP);
-
-#     ifdef PNG_COLORSPACE_SUPPORTED
-      /* Clean up the iCCP profile now if it won't be used. */
-      png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/);
-#     else
-      PNG_UNUSED(png_ptr)
-#     endif
-   }
-
-   else
-   {
-#     ifdef PNG_COLORSPACE_SUPPORTED
-      /* Leave the INFO_iCCP flag set if the pngset.c code has already set
-       * it; this allows a PNG to contain a profile which matches sRGB and
-       * yet still have that profile retrievable by the application.
-       */
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0)
-         info_ptr->valid |= PNG_INFO_sRGB;
-
-      else
-         info_ptr->valid &= ~PNG_INFO_sRGB;
-
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-         info_ptr->valid |= PNG_INFO_cHRM;
-
-      else
-         info_ptr->valid &= ~PNG_INFO_cHRM;
-#     endif
-
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0)
-         info_ptr->valid |= PNG_INFO_gAMA;
-
-      else
-         info_ptr->valid &= ~PNG_INFO_gAMA;
-   }
-}
-
-#ifdef PNG_READ_SUPPORTED
-void /* PRIVATE */
-png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr)
-{
-   if (info_ptr == NULL) /* reduce code size; check here not in the caller */
-      return;
-
-   info_ptr->colorspace = png_ptr->colorspace;
-   png_colorspace_sync_info(png_ptr, info_ptr);
-}
-#endif
-#endif /* GAMMA */
-
 #ifdef PNG_COLORSPACE_SUPPORTED
 static png_int_32
 png_fp_add(png_int_32 addend0, png_int_32 addend1, int *error)
@@ -1269,9 +1128,10 @@ png_safe_add(png_int_32 *addend0_and_result, png_int_32 addend1,
  * non-zero on a parameter error.  The X, Y and Z values are required to be
  * positive and less than 1.0.
  */
-static int
+int /* PRIVATE */
 png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
 {
+   /* NOTE: returns 0 on success, 1 means error. */
    png_int_32 d, dred, dgreen, dblue, dwhite, whiteX, whiteY;
 
    /* 'd' in each of the blocks below is just X+Y+Z for each component,
@@ -1334,9 +1194,10 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
    return 0;
 }
 
-static int
+int /* PRIVATE */
 png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
 {
+   /* NOTE: returns 0 on success, 1 means error. */
    png_fixed_point red_inverse, green_inverse, blue_scale;
    png_fixed_point left, right, denominator;
 
@@ -1628,239 +1489,9 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
 
    return 0; /*success*/
 }
+#endif /* COLORSPACE */
 
-static int
-png_XYZ_normalize(png_XYZ *XYZ)
-{
-   png_int_32 Y, Ytemp;
-
-   /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. */
-   Ytemp = XYZ->red_Y;
-   if (png_safe_add(&Ytemp, XYZ->green_Y, XYZ->blue_Y))
-      return 1;
-
-   Y = Ytemp;
-
-   if (Y != PNG_FP_1)
-   {
-      if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0)
-         return 1;
-
-      if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0)
-         return 1;
-
-      if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0)
-         return 1;
-   }
-
-   return 0;
-}
-
-static int
-png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta)
-{
-   /* Allow an error of +/-0.01 (absolute value) on each chromaticity */
-   if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) ||
-       PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) ||
-       PNG_OUT_OF_RANGE(xy1->redx,   xy2->redx,  delta) ||
-       PNG_OUT_OF_RANGE(xy1->redy,   xy2->redy,  delta) ||
-       PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) ||
-       PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) ||
-       PNG_OUT_OF_RANGE(xy1->bluex,  xy2->bluex, delta) ||
-       PNG_OUT_OF_RANGE(xy1->bluey,  xy2->bluey, delta))
-      return 0;
-   return 1;
-}
-
-/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM
- * chunk chromaticities.  Earlier checks used to simply look for the overflow
- * condition (where the determinant of the matrix to solve for XYZ ends up zero
- * because the chromaticity values are not all distinct.)  Despite this it is
- * theoretically possible to produce chromaticities that are apparently valid
- * but that rapidly degrade to invalid, potentially crashing, sets because of
- * arithmetic inaccuracies when calculations are performed on them.  The new
- * check is to round-trip xy -> XYZ -> xy and then check that the result is
- * within a small percentage of the original.
- */
-static int
-png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy)
-{
-   int result;
-   png_xy xy_test;
-
-   /* As a side-effect this routine also returns the XYZ endpoints. */
-   result = png_XYZ_from_xy(XYZ, xy);
-   if (result != 0)
-      return result;
-
-   result = png_xy_from_XYZ(&xy_test, XYZ);
-   if (result != 0)
-      return result;
-
-   if (png_colorspace_endpoints_match(xy, &xy_test,
-       5/*actually, the math is pretty accurate*/) != 0)
-      return 0;
-
-   /* Too much slip */
-   return 1;
-}
-
-/* This is the check going the other way.  The XYZ is modified to normalize it
- * (another side-effect) and the xy chromaticities are returned.
- */
-static int
-png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ)
-{
-   int result;
-   png_XYZ XYZtemp;
-
-   result = png_XYZ_normalize(XYZ);
-   if (result != 0)
-      return result;
-
-   result = png_xy_from_XYZ(xy, XYZ);
-   if (result != 0)
-      return result;
-
-   XYZtemp = *XYZ;
-   return png_colorspace_check_xy(&XYZtemp, xy);
-}
-
-/* Used to check for an endpoint match against sRGB */
-static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */
-{
-   /* color      x       y */
-   /* red   */ 64000, 33000,
-   /* green */ 30000, 60000,
-   /* blue  */ 15000,  6000,
-   /* white */ 31270, 32900
-};
-
-static int
-png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,
-    int preferred)
-{
-   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return 0;
-
-   /* The consistency check is performed on the chromaticities; this factors out
-    * variations because of the normalization (or not) of the end point Y
-    * values.
-    */
-   if (preferred < 2 &&
-       (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      /* The end points must be reasonably close to any we already have.  The
-       * following allows an error of up to +/-.001
-       */
-      if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy,
-          100) == 0)
-      {
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_benign_error(png_ptr, "inconsistent chromaticities");
-         return 0; /* failed */
-      }
-
-      /* Only overwrite with preferred values */
-      if (preferred == 0)
-         return 1; /* ok, but no change */
-   }
-
-   colorspace->end_points_xy = *xy;
-   colorspace->end_points_XYZ = *XYZ;
-   colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS;
-
-   /* The end points are normally quoted to two decimal digits, so allow +/-0.01
-    * on this test.
-    */
-   if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0)
-      colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB;
-
-   else
-      colorspace->flags &= PNG_COLORSPACE_CANCEL(
-         PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);
-
-   return 2; /* ok and changed */
-}
-
-int /* PRIVATE */
-png_colorspace_set_chromaticities(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, const png_xy *xy, int preferred)
-{
-   /* We must check the end points to ensure they are reasonable - in the past
-    * color management systems have crashed as a result of getting bogus
-    * colorant values, while this isn't the fault of libpng it is the
-    * responsibility of libpng because PNG carries the bomb and libpng is in a
-    * position to protect against it.
-    */
-   png_XYZ XYZ;
-
-   switch (png_colorspace_check_xy(&XYZ, xy))
-   {
-      case 0: /* success */
-         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ,
-             preferred);
-
-      case 1:
-         /* We can't invert the chromaticities so we can't produce value XYZ
-          * values.  Likely as not a color management system will fail too.
-          */
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_benign_error(png_ptr, "invalid chromaticities");
-         break;
-
-      default:
-         /* libpng is broken; this should be a warning but if it happens we
-          * want error reports so for the moment it is an error.
-          */
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_error(png_ptr, "internal error checking chromaticities");
-   }
-
-   return 0; /* failed */
-}
-
-int /* PRIVATE */
-png_colorspace_set_endpoints(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
-{
-   png_XYZ XYZ = *XYZ_in;
-   png_xy xy;
-
-   switch (png_colorspace_check_XYZ(&xy, &XYZ))
-   {
-      case 0:
-         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ,
-             preferred);
-
-      case 1:
-         /* End points are invalid. */
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_benign_error(png_ptr, "invalid end points");
-         break;
-
-      default:
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_error(png_ptr, "internal error checking chromaticities");
-   }
-
-   return 0; /* failed */
-}
-
-#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED)
+#ifdef PNG_iCCP_SUPPORTED
 /* Error message generation */
 static char
 png_icc_tag_char(png_uint_32 byte)
@@ -1900,15 +1531,12 @@ is_ICC_signature(png_alloc_size_t it)
 }
 
 static int
-png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_alloc_size_t value, png_const_charp reason)
+png_icc_profile_error(png_const_structrp png_ptr, png_const_charp name,
+   png_alloc_size_t value, png_const_charp reason)
 {
    size_t pos;
    char message[196]; /* see below for calculation */
 
-   if (colorspace != NULL)
-      colorspace->flags |= PNG_COLORSPACE_INVALID;
-
    pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */
    pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */
    pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */
@@ -1935,109 +1563,13 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
    pos = png_safecat(message, (sizeof message), pos, reason);
    PNG_UNUSED(pos)
 
-   /* This is recoverable, but make it unconditionally an app_error on write to
-    * avoid writing invalid ICC profiles into PNG files (i.e., we handle them
-    * on read, with a warning, but on write unless the app turns off
-    * application errors the PNG won't be written.)
-    */
-   png_chunk_report(png_ptr, message,
-       (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
+   png_chunk_benign_error(png_ptr, message);
 
    return 0;
 }
-#endif /* sRGB || iCCP */
-
-#ifdef PNG_sRGB_SUPPORTED
-int /* PRIVATE */
-png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    int intent)
-{
-   /* sRGB sets known gamma, end points and (from the chunk) intent. */
-   /* IMPORTANT: these are not necessarily the values found in an ICC profile
-    * because ICC profiles store values adapted to a D50 environment; it is
-    * expected that the ICC profile mediaWhitePointTag will be D50; see the
-    * checks and code elsewhere to understand this better.
-    *
-    * These XYZ values, which are accurate to 5dp, produce rgb to gray
-    * coefficients of (6968,23435,2366), which are reduced (because they add up
-    * to 32769 not 32768) to (6968,23434,2366).  These are the values that
-    * libpng has traditionally used (and are the best values given the 15bit
-    * algorithm used by the rgb to gray code.)
-    */
-   static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */
-   {
-      /* color      X      Y      Z */
-      /* red   */ 41239, 21264,  1933,
-      /* green */ 35758, 71517, 11919,
-      /* blue  */ 18048,  7219, 95053
-   };
-
-   /* Do nothing if the colorspace is already invalidated. */
-   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return 0;
-
-   /* Check the intent, then check for existing settings.  It is valid for the
-    * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must
-    * be consistent with the correct values.  If, however, this function is
-    * called below because an iCCP chunk matches sRGB then it is quite
-    * conceivable that an older app recorded incorrect gAMA and cHRM because of
-    * an incorrect calculation based on the values in the profile - this does
-    * *not* invalidate the profile (though it still produces an error, which can
-    * be ignored.)
-    */
-   if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST)
-      return png_icc_profile_error(png_ptr, colorspace, "sRGB",
-          (png_alloc_size_t)intent, "invalid sRGB rendering intent");
-
-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 &&
-       colorspace->rendering_intent != intent)
-      return png_icc_profile_error(png_ptr, colorspace, "sRGB",
-         (png_alloc_size_t)intent, "inconsistent rendering intents");
-
-   if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0)
-   {
-      png_benign_error(png_ptr, "duplicate sRGB information ignored");
-      return 0;
-   }
-
-   /* If the standard sRGB cHRM chunk does not match the one from the PNG file
-    * warn but overwrite the value with the correct one.
-    */
-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 &&
-       !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
-       100))
-      png_chunk_report(png_ptr, "cHRM chunk does not match sRGB",
-         PNG_CHUNK_ERROR);
-
-   /* This check is just done for the error reporting - the routine always
-    * returns true when the 'from' argument corresponds to sRGB (2).
-    */
-   (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE,
-       2/*from sRGB*/);
-
-   /* intent: bugs in GCC force 'int' to be used as the parameter type. */
-   colorspace->rendering_intent = (png_uint_16)intent;
-   colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT;
-
-   /* endpoints */
-   colorspace->end_points_xy = sRGB_xy;
-   colorspace->end_points_XYZ = sRGB_XYZ;
-   colorspace->flags |=
-      (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);
-
-   /* gamma */
-   colorspace->gamma = PNG_GAMMA_sRGB_INVERSE;
-   colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA;
-
-   /* Finally record that we have an sRGB profile */
-   colorspace->flags |=
-      (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB);
-
-   return 1; /* set */
-}
-#endif /* sRGB */
+#endif /* iCCP */
 
-#ifdef PNG_iCCP_SUPPORTED
+#ifdef PNG_READ_iCCP_SUPPORTED
 /* Encoded value of D50 as an ICC XYZNumber.  From the ICC 2010 spec the value
  * is XYZ(0.9642,1.0,0.8249), which scales to:
  *
@@ -2047,21 +1579,19 @@ static const png_byte D50_nCIEXYZ[12] =
    { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d };
 
 static int /* bool */
-icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length)
+icc_check_length(png_const_structrp png_ptr, png_const_charp name,
+   png_uint_32 profile_length)
 {
    if (profile_length < 132)
-      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-          "too short");
+      return png_icc_profile_error(png_ptr, name, profile_length, "too short");
    return 1;
 }
 
-#ifdef PNG_READ_iCCP_SUPPORTED
 int /* PRIVATE */
-png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length)
+png_icc_check_length(png_const_structrp png_ptr, png_const_charp name,
+   png_uint_32 profile_length)
 {
-   if (!icc_check_length(png_ptr, colorspace, name, profile_length))
+   if (!icc_check_length(png_ptr, name, profile_length))
       return 0;
 
    /* This needs to be here because the 'normal' check is in
@@ -2070,30 +1600,17 @@ png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
     * the caller supplies the profile buffer so libpng doesn't allocate it.  See
     * the call to icc_check_length below (the write case).
     */
-#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
-      else if (png_ptr->user_chunk_malloc_max > 0 &&
-               png_ptr->user_chunk_malloc_max < profile_length)
-         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-             "exceeds application limits");
-#  elif PNG_USER_CHUNK_MALLOC_MAX > 0
-      else if (PNG_USER_CHUNK_MALLOC_MAX < profile_length)
-         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-             "exceeds libpng limits");
-#  else /* !SET_USER_LIMITS */
-      /* This will get compiled out on all 32-bit and better systems. */
-      else if (PNG_SIZE_MAX < profile_length)
-         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-             "exceeds system limits");
-#  endif /* !SET_USER_LIMITS */
+   if (profile_length > png_chunk_max(png_ptr))
+      return png_icc_profile_error(png_ptr, name, profile_length,
+            "profile too long");
 
    return 1;
 }
-#endif /* READ_iCCP */
 
 int /* PRIVATE */
-png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length,
-    png_const_bytep profile/* first 132 bytes only */, int color_type)
+png_icc_check_header(png_const_structrp png_ptr, png_const_charp name,
+   png_uint_32 profile_length,
+   png_const_bytep profile/* first 132 bytes only */, int color_type)
 {
    png_uint_32 temp;
 
@@ -2104,18 +1621,18 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
     */
    temp = png_get_uint_32(profile);
    if (temp != profile_length)
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+      return png_icc_profile_error(png_ptr, name, temp,
           "length does not match profile");
 
    temp = (png_uint_32) (*(profile+8));
    if (temp > 3 && (profile_length & 3))
-      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
+      return png_icc_profile_error(png_ptr, name, profile_length,
           "invalid length");
 
    temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */
    if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */
       profile_length < 132+12*temp) /* truncated tag table */
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+      return png_icc_profile_error(png_ptr, name, temp,
           "tag count too large");
 
    /* The 'intent' must be valid or we can't store it, ICC limits the intent to
@@ -2123,14 +1640,14 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
     */
    temp = png_get_uint_32(profile+64);
    if (temp >= 0xffff) /* The ICC limit */
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+      return png_icc_profile_error(png_ptr, name, temp,
           "invalid rendering intent");
 
    /* This is just a warning because the profile may be valid in future
     * versions.
     */
    if (temp >= PNG_sRGB_INTENT_LAST)
-      (void)png_icc_profile_error(png_ptr, NULL, name, temp,
+      (void)png_icc_profile_error(png_ptr, name, temp,
           "intent outside defined range");
 
    /* At this point the tag table can't be checked because it hasn't necessarily
@@ -2147,7 +1664,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
     */
    temp = png_get_uint_32(profile+36); /* signature 'ascp' */
    if (temp != 0x61637370)
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+      return png_icc_profile_error(png_ptr, name, temp,
           "invalid signature");
 
    /* Currently the PCS illuminant/adopted white point (the computational
@@ -2158,7 +1675,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
     * following is just a warning.
     */
    if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0)
-      (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/,
+      (void)png_icc_profile_error(png_ptr, name, 0/*no tag value*/,
           "PCS illuminant is not D50");
 
    /* The PNG spec requires this:
@@ -2186,18 +1703,18 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
    {
       case 0x52474220: /* 'RGB ' */
          if ((color_type & PNG_COLOR_MASK_COLOR) == 0)
-            return png_icc_profile_error(png_ptr, colorspace, name, temp,
+            return png_icc_profile_error(png_ptr, name, temp,
                 "RGB color space not permitted on grayscale PNG");
          break;
 
       case 0x47524159: /* 'GRAY' */
          if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
-            return png_icc_profile_error(png_ptr, colorspace, name, temp,
+            return png_icc_profile_error(png_ptr, name, temp,
                 "Gray color space not permitted on RGB PNG");
          break;
 
       default:
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         return png_icc_profile_error(png_ptr, name, temp,
              "invalid ICC profile color space");
    }
 
@@ -2222,7 +1739,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
 
       case 0x61627374: /* 'abst' */
          /* May not be embedded in an image */
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         return png_icc_profile_error(png_ptr, name, temp,
              "invalid embedded Abstract ICC profile");
 
       case 0x6c696e6b: /* 'link' */
@@ -2232,7 +1749,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
           * therefore a DeviceLink profile should not be found embedded in a
           * PNG.
           */
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         return png_icc_profile_error(png_ptr, name, temp,
              "unexpected DeviceLink ICC profile class");
 
       case 0x6e6d636c: /* 'nmcl' */
@@ -2240,7 +1757,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
           * contain an AToB0 tag that is open to misinterpretation.  Almost
           * certainly it will fail the tests below.
           */
-         (void)png_icc_profile_error(png_ptr, NULL, name, temp,
+         (void)png_icc_profile_error(png_ptr, name, temp,
              "unexpected NamedColor ICC profile class");
          break;
 
@@ -2250,7 +1767,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
           * tag content to ensure they are backward compatible with one of the
           * understood profiles.
           */
-         (void)png_icc_profile_error(png_ptr, NULL, name, temp,
+         (void)png_icc_profile_error(png_ptr, name, temp,
              "unrecognized ICC profile class");
          break;
    }
@@ -2266,7 +1783,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
          break;
 
       default:
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         return png_icc_profile_error(png_ptr, name, temp,
              "unexpected ICC PCS encoding");
    }
 
@@ -2274,9 +1791,9 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
 }
 
 int /* PRIVATE */
-png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length,
-    png_const_bytep profile /* header plus whole tag table */)
+png_icc_check_tag_table(png_const_structrp png_ptr, png_const_charp name,
+   png_uint_32 profile_length,
+   png_const_bytep profile /* header plus whole tag table */)
 {
    png_uint_32 tag_count = png_get_uint_32(profile+128);
    png_uint_32 itag;
@@ -2302,7 +1819,7 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
        * profile.
        */
       if (tag_start > profile_length || tag_length > profile_length - tag_start)
-         return png_icc_profile_error(png_ptr, colorspace, name, tag_id,
+         return png_icc_profile_error(png_ptr, name, tag_id,
              "ICC profile tag outside profile");
 
       if ((tag_start & 3) != 0)
@@ -2311,307 +1828,132 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
           * only a warning here because libpng does not care about the
           * alignment.
           */
-         (void)png_icc_profile_error(png_ptr, NULL, name, tag_id,
+         (void)png_icc_profile_error(png_ptr, name, tag_id,
              "ICC profile tag start not a multiple of 4");
       }
    }
 
    return 1; /* success, maybe with warnings */
 }
+#endif /* READ_iCCP */
 
-#ifdef PNG_sRGB_SUPPORTED
-#if PNG_sRGB_PROFILE_CHECKS >= 0
-/* Information about the known ICC sRGB profiles */
-static const struct
-{
-   png_uint_32 adler, crc, length;
-   png_uint_32 md5[4];
-   png_byte    have_md5;
-   png_byte    is_broken;
-   png_uint_16 intent;
-
-#  define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0)
-#  define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\
-      { adler, crc, length, md5, broke, intent },
-
-} png_sRGB_checks[] =
-{
-   /* This data comes from contrib/tools/checksum-icc run on downloads of
-    * all four ICC sRGB profiles from www.color.org.
-    */
-   /* adler32, crc32, MD5[4], intent, date, length, file-name */
-   PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9,
-       PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
-       "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
-
-   /* ICC sRGB v2 perceptual no black-compensation: */
-   PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21,
-       PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
-       "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
-
-   PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae,
-       PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
-       "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
-
-   /* ICC sRGB v4 perceptual */
-   PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812,
-       PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
-       "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
-
-   /* The following profiles have no known MD5 checksum. If there is a match
-    * on the (empty) MD5 the other fields are used to attempt a match and
-    * a warning is produced.  The first two of these profiles have a 'cprt' tag
-    * which suggests that they were also made by Hewlett Packard.
-    */
-   PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce,
-       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
-       "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
-
-   /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not
-    * match the D50 PCS illuminant in the header (it is in fact the D65 values,
-    * so the white point is recorded as the un-adapted value.)  The profiles
-    * below only differ in one byte - the intent - and are basically the same as
-    * the previous profile except for the mediaWhitePointTag error and a missing
-    * chromaticAdaptationTag.
-    */
-   PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552,
-       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
-       "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
-
-   PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,
-       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
-       "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
-};
-
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+#if (defined PNG_READ_mDCV_SUPPORTED) || (defined PNG_READ_cHRM_SUPPORTED)
 static int
-png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
-    png_const_bytep profile, uLong adler)
+have_chromaticities(png_const_structrp png_ptr)
 {
-   /* The quick check is to verify just the MD5 signature and trust the
-    * rest of the data.  Because the profile has already been verified for
-    * correctness this is safe.  png_colorspace_set_sRGB will check the 'intent'
-    * field too, so if the profile has been edited with an intent not defined
-    * by sRGB (but maybe defined by a later ICC specification) the read of
-    * the profile will fail at that point.
+   /* Handle new PNGv3 chunks and the precedence rules to determine whether
+    * png_struct::chromaticities must be processed.  Only required for RGB to
+    * gray.
+    *
+    * mDCV: this is the mastering colour space and it is independent of the
+    *       encoding so it needs to be used regardless of the encoded space.
+    *
+    * cICP: first in priority but not yet implemented - the chromaticities come
+    *       from the 'primaries'.
+    *
+    * iCCP: not supported by libpng (so ignored)
+    *
+    * sRGB: the defaults match sRGB
+    *
+    * cHRM: calculate the coefficients
     */
+#  ifdef PNG_READ_mDCV_SUPPORTED
+      if (png_has_chunk(png_ptr, mDCV))
+         return 1;
+#     define check_chromaticities 1
+#  endif /*mDCV*/
 
-   png_uint_32 length = 0;
-   png_uint_32 intent = 0x10000; /* invalid */
-#if PNG_sRGB_PROFILE_CHECKS > 1
-   uLong crc = 0; /* the value for 0 length data */
-#endif
-   unsigned int i;
-
-#ifdef PNG_SET_OPTION_SUPPORTED
-   /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */
-   if (((png_ptr->options >> PNG_SKIP_sRGB_CHECK_PROFILE) & 3) ==
-               PNG_OPTION_ON)
-      return 0;
-#endif
-
-   for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i)
-   {
-      if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] &&
-         png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] &&
-         png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] &&
-         png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3])
-      {
-         /* This may be one of the old HP profiles without an MD5, in that
-          * case we can only use the length and Adler32 (note that these
-          * are not used by default if there is an MD5!)
-          */
-#        if PNG_sRGB_PROFILE_CHECKS == 0
-            if (png_sRGB_checks[i].have_md5 != 0)
-               return 1+png_sRGB_checks[i].is_broken;
-#        endif
-
-         /* Profile is unsigned or more checks have been configured in. */
-         if (length == 0)
-         {
-            length = png_get_uint_32(profile);
-            intent = png_get_uint_32(profile+64);
-         }
-
-         /* Length *and* intent must match */
-         if (length == (png_uint_32) png_sRGB_checks[i].length &&
-            intent == (png_uint_32) png_sRGB_checks[i].intent)
-         {
-            /* Now calculate the adler32 if not done already. */
-            if (adler == 0)
-            {
-               adler = adler32(0, NULL, 0);
-               adler = adler32(adler, profile, length);
-            }
-
-            if (adler == png_sRGB_checks[i].adler)
-            {
-               /* These basic checks suggest that the data has not been
-                * modified, but if the check level is more than 1 perform
-                * our own crc32 checksum on the data.
-                */
-#              if PNG_sRGB_PROFILE_CHECKS > 1
-                  if (crc == 0)
-                  {
-                     crc = crc32(0, NULL, 0);
-                     crc = crc32(crc, profile, length);
-                  }
-
-                  /* So this check must pass for the 'return' below to happen.
-                   */
-                  if (crc == png_sRGB_checks[i].crc)
-#              endif
-               {
-                  if (png_sRGB_checks[i].is_broken != 0)
-                  {
-                     /* These profiles are known to have bad data that may cause
-                      * problems if they are used, therefore attempt to
-                      * discourage their use, skip the 'have_md5' warning below,
-                      * which is made irrelevant by this error.
-                      */
-                     png_chunk_report(png_ptr, "known incorrect sRGB profile",
-                         PNG_CHUNK_ERROR);
-                  }
-
-                  /* Warn that this being done; this isn't even an error since
-                   * the profile is perfectly valid, but it would be nice if
-                   * people used the up-to-date ones.
-                   */
-                  else if (png_sRGB_checks[i].have_md5 == 0)
-                  {
-                     png_chunk_report(png_ptr,
-                         "out-of-date sRGB profile with no signature",
-                         PNG_CHUNK_WARNING);
-                  }
+#  ifdef PNG_READ_sRGB_SUPPORTED
+      if (png_has_chunk(png_ptr, sRGB))
+         return 0;
+#  endif /*sRGB*/
 
-                  return 1+png_sRGB_checks[i].is_broken;
-               }
-            }
-
-# if PNG_sRGB_PROFILE_CHECKS > 0
-         /* The signature matched, but the profile had been changed in some
-          * way.  This probably indicates a data error or uninformed hacking.
-          * Fall through to "no match".
-          */
-         png_chunk_report(png_ptr,
-             "Not recognizing known sRGB profile that has been edited",
-             PNG_CHUNK_WARNING);
-         break;
-# endif
-         }
-      }
-   }
+#  ifdef PNG_READ_cHRM_SUPPORTED
+      if (png_has_chunk(png_ptr, cHRM))
+         return 1;
+#     define check_chromaticities 1
+#  endif /*cHRM*/
 
-   return 0; /* no match */
+   return 0; /* sRGB defaults */
 }
+#endif /* READ_mDCV || READ_cHRM */
 
 void /* PRIVATE */
-png_icc_set_sRGB(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
+png_set_rgb_coefficients(png_structrp png_ptr)
 {
-   /* Is this profile one of the known ICC sRGB profiles?  If it is, just set
-    * the sRGB information.
+   /* Set the rgb_to_gray coefficients from the colorspace if available.  Note
+    * that '_set' means that png_rgb_to_gray was called **and** it successfully
+    * set up the coefficients.
     */
-   if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0)
-      (void)png_colorspace_set_sRGB(png_ptr, colorspace,
-         (int)/*already checked*/png_get_uint_32(profile+64));
-}
-#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */
-#endif /* sRGB */
-
-int /* PRIVATE */
-png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,
-    int color_type)
-{
-   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return 0;
-
-   if (icc_check_length(png_ptr, colorspace, name, profile_length) != 0 &&
-       png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,
-           color_type) != 0 &&
-       png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,
-           profile) != 0)
+   if (png_ptr->rgb_to_gray_coefficients_set == 0)
    {
-#     if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
-         /* If no sRGB support, don't try storing sRGB information */
-         png_icc_set_sRGB(png_ptr, colorspace, profile, 0);
-#     endif
-      return 1;
-   }
+#  if check_chromaticities
+      png_XYZ xyz;
 
-   /* Failure case */
-   return 0;
-}
-#endif /* iCCP */
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-void /* PRIVATE */
-png_colorspace_set_rgb_coefficients(png_structrp png_ptr)
-{
-   /* Set the rgb_to_gray coefficients from the colorspace. */
-   if (png_ptr->rgb_to_gray_coefficients_set == 0 &&
-      (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      /* png_set_background has not been called, get the coefficients from the Y
-       * values of the colorspace colorants.
-       */
-      png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y;
-      png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y;
-      png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y;
-      png_fixed_point total = r+g+b;
-
-      if (total > 0 &&
-         r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 &&
-         g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 &&
-         b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 &&
-         r+g+b <= 32769)
+      if (have_chromaticities(png_ptr) &&
+          png_XYZ_from_xy(&xyz, &png_ptr->chromaticities) == 0)
       {
-         /* We allow 0 coefficients here.  r+g+b may be 32769 if two or
-          * all of the coefficients were rounded up.  Handle this by
-          * reducing the *largest* coefficient by 1; this matches the
-          * approach used for the default coefficients in pngrtran.c
+         /* png_set_rgb_to_gray has not set the coefficients, get them from the
+          * Y * values of the colorspace colorants.
           */
-         int add = 0;
+         png_fixed_point r = xyz.red_Y;
+         png_fixed_point g = xyz.green_Y;
+         png_fixed_point b = xyz.blue_Y;
+         png_fixed_point total = r+g+b;
+
+         if (total > 0 &&
+            r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 &&
+            g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 &&
+            b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 &&
+            r+g+b <= 32769)
+         {
+            /* We allow 0 coefficients here.  r+g+b may be 32769 if two or
+             * all of the coefficients were rounded up.  Handle this by
+             * reducing the *largest* coefficient by 1; this matches the
+             * approach used for the default coefficients in pngrtran.c
+             */
+            int add = 0;
 
-         if (r+g+b > 32768)
-            add = -1;
-         else if (r+g+b < 32768)
-            add = 1;
+            if (r+g+b > 32768)
+               add = -1;
+            else if (r+g+b < 32768)
+               add = 1;
 
-         if (add != 0)
-         {
-            if (g >= r && g >= b)
-               g += add;
-            else if (r >= g && r >= b)
-               r += add;
-            else
-               b += add;
-         }
+            if (add != 0)
+            {
+               if (g >= r && g >= b)
+                  g += add;
+               else if (r >= g && r >= b)
+                  r += add;
+               else
+                  b += add;
+            }
 
-         /* Check for an internal error. */
-         if (r+g+b != 32768)
-            png_error(png_ptr,
-                "internal error handling cHRM coefficients");
+            /* Check for an internal error. */
+            if (r+g+b != 32768)
+               png_error(png_ptr,
+                   "internal error handling cHRM coefficients");
 
-         else
-         {
-            png_ptr->rgb_to_gray_red_coeff   = (png_uint_16)r;
-            png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g;
+            else
+            {
+               png_ptr->rgb_to_gray_red_coeff   = (png_uint_16)r;
+               png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g;
+            }
          }
       }
-
-      /* This is a png_error at present even though it could be ignored -
-       * it should never happen, but it is important that if it does, the
-       * bug is fixed.
-       */
       else
-         png_error(png_ptr, "internal error handling cHRM->XYZ");
+#  endif /* check_chromaticities */
+      {
+         /* Use the historical REC 709 (etc) values: */
+         png_ptr->rgb_to_gray_red_coeff   = 6968;
+         png_ptr->rgb_to_gray_green_coeff = 23434;
+         /* png_ptr->rgb_to_gray_blue_coeff  = 2366; */
+      }
    }
 }
 #endif /* READ_RGB_TO_GRAY */
 
-#endif /* COLORSPACE */
-
 void /* PRIVATE */
 png_check_IHDR(png_const_structrp png_ptr,
     png_uint_32 width, png_uint_32 height, int bit_depth,
@@ -3413,7 +2755,7 @@ png_fixed_ITU(png_const_structrp png_ptr, double fp, png_const_charp text)
 #endif
 
 
-#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\
     defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)
 /* muldiv functions */
 /* This API takes signed arguments and rounds the result to the nearest
@@ -3421,7 +2763,7 @@ png_fixed_ITU(png_const_structrp png_ptr, double fp, png_const_charp text)
  * the nearest .00001).  Overflow and divide by zero are signalled in
  * the result, a boolean - true on success, false on overflow.
  */
-int
+int /* PRIVATE */
 png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
     png_int_32 divisor)
 {
@@ -3535,27 +2877,7 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
 
    return 0;
 }
-#endif /* READ_GAMMA || INCH_CONVERSIONS */
-
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
-/* The following is for when the caller doesn't much care about the
- * result.
- */
-png_fixed_point
-png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times,
-    png_int_32 divisor)
-{
-   png_fixed_point result;
 
-   if (png_muldiv(&result, a, times, divisor) != 0)
-      return result;
-
-   png_warning(png_ptr, "fixed point overflow ignored");
-   return 0;
-}
-#endif
-
-#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */
 /* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
 png_fixed_point
 png_reciprocal(png_fixed_point a)
@@ -3574,26 +2896,38 @@ png_reciprocal(png_fixed_point a)
 
    return 0; /* error/overflow */
 }
+#endif /* READ_GAMMA || COLORSPACE || INCH_CONVERSIONS || READ_pHYS */
 
+#ifdef PNG_READ_GAMMA_SUPPORTED
 /* This is the shared test on whether a gamma value is 'significant' - whether
  * it is worth doing gamma correction.
  */
 int /* PRIVATE */
 png_gamma_significant(png_fixed_point gamma_val)
 {
+   /* sRGB:       1/2.2 == 0.4545(45)
+    * AdobeRGB:   1/(2+51/256) ~= 0.45471 5dp
+    *
+    * So the correction from AdobeRGB to sRGB (output) is:
+    *
+    *    2.2/(2+51/256) == 1.00035524
+    *
+    * I.e. vanishly small (<4E-4) but still detectable in 16-bit linear (+/-
+    * 23).  Note that the Adobe choice seems to be something intended to give an
+    * exact number with 8 binary fractional digits - it is the closest to 2.2
+    * that is possible a base 2 .8p representation.
+    */
    return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED ||
        gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED;
 }
-#endif
 
-#ifdef PNG_READ_GAMMA_SUPPORTED
-#ifdef PNG_16BIT_SUPPORTED
+#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
 /* A local convenience routine. */
 static png_fixed_point
 png_product2(png_fixed_point a, png_fixed_point b)
 {
-   /* The required result is 1/a * 1/b; the following preserves accuracy. */
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+   /* The required result is a * b; the following preserves accuracy. */
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED /* Should now be unused */
    double r = a * 1E-5;
    r *= b;
    r = floor(r+.5);
@@ -3609,9 +2943,8 @@ png_product2(png_fixed_point a, png_fixed_point b)
 
    return 0; /* overflow */
 }
-#endif /* 16BIT */
+#endif /* FLOATING_ARITHMETIC */
 
-/* The inverse of the above. */
 png_fixed_point
 png_reciprocal2(png_fixed_point a, png_fixed_point b)
 {
@@ -4264,10 +3597,27 @@ png_destroy_gamma_table(png_structrp png_ptr)
  * tables, we don't make a full table if we are reducing to 8-bit in
  * the future.  Note also how the gamma_16 tables are segmented so that
  * we don't need to allocate > 64K chunks for a full 16-bit table.
+ *
+ * TODO: move this to pngrtran.c and make it static.  Better yet create
+ * pngcolor.c and put all the PNG_COLORSPACE stuff in there.
  */
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+#  define GAMMA_TRANSFORMS 1 /* #ifdef CSE */
+#else
+#  define GAMMA_TRANSFORMS 0
+#endif
+
 void /* PRIVATE */
 png_build_gamma_table(png_structrp png_ptr, int bit_depth)
 {
+   png_fixed_point file_gamma, screen_gamma;
+   png_fixed_point correction;
+#  if GAMMA_TRANSFORMS
+      png_fixed_point file_to_linear, linear_to_screen;
+#  endif
+
    png_debug(1, "in png_build_gamma_table");
 
    /* Remove any existing table; this copes with multiple calls to
@@ -4282,27 +3632,44 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth)
       png_destroy_gamma_table(png_ptr);
    }
 
+   /* The following fields are set, finally, in png_init_read_transformations.
+    * If file_gamma is 0 (unset) nothing can be done otherwise if screen_gamma
+    * is 0 (unset) there is no gamma correction but to/from linear is possible.
+    */
+   file_gamma = png_ptr->file_gamma;
+   screen_gamma = png_ptr->screen_gamma;
+#  if GAMMA_TRANSFORMS
+      file_to_linear = png_reciprocal(file_gamma);
+#  endif
+
+   if (screen_gamma > 0)
+   {
+#     if GAMMA_TRANSFORMS
+         linear_to_screen = png_reciprocal(screen_gamma);
+#     endif
+      correction = png_reciprocal2(screen_gamma, file_gamma);
+   }
+   else /* screen gamma unknown */
+   {
+#     if GAMMA_TRANSFORMS
+         linear_to_screen = file_gamma;
+#     endif
+      correction = PNG_FP_1;
+   }
+
    if (bit_depth <= 8)
    {
-      png_build_8bit_table(png_ptr, &png_ptr->gamma_table,
-          png_ptr->screen_gamma > 0 ?
-          png_reciprocal2(png_ptr->colorspace.gamma,
-          png_ptr->screen_gamma) : PNG_FP_1);
+      png_build_8bit_table(png_ptr, &png_ptr->gamma_table, correction);
 
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+#if GAMMA_TRANSFORMS
       if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
       {
-         png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,
-             png_reciprocal(png_ptr->colorspace.gamma));
+         png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, file_to_linear);
 
          png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1,
-             png_ptr->screen_gamma > 0 ?
-             png_reciprocal(png_ptr->screen_gamma) :
-             png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
+            linear_to_screen);
       }
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
+#endif /* GAMMA_TRANSFORMS */
    }
 #ifdef PNG_16BIT_SUPPORTED
    else
@@ -4368,32 +3735,26 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth)
        * reduced to 8 bits.
        */
       if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
-          png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
-          png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma,
-          png_ptr->screen_gamma) : PNG_FP_1);
-
+         png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
+            png_reciprocal(correction));
       else
-          png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,
-          png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma,
-          png_ptr->screen_gamma) : PNG_FP_1);
+         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,
+            correction);
 
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+#  if GAMMA_TRANSFORMS
       if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
       {
          png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,
-             png_reciprocal(png_ptr->colorspace.gamma));
+            file_to_linear);
 
          /* Notice that the '16 from 1' table should be full precision, however
           * the lookup on this table still uses gamma_shift, so it can't be.
           * TODO: fix this.
           */
          png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift,
-             png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
-             png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
+            linear_to_screen);
       }
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
+#endif /* GAMMA_TRANSFORMS */
    }
 #endif /* 16BIT */
 }
diff --git a/source/libs/libpng/libpng-src/png.h b/source/libs/libpng/libpng-src/png.h
index 6fd6129b9..9b069e4ee 100644
--- a/source/libs/libpng/libpng-src/png.h
+++ b/source/libs/libpng/libpng-src/png.h
@@ -1,6 +1,6 @@
 /* png.h - header file for PNG reference library
  *
- * libpng version 1.6.46
+ * libpng version 1.6.47
  *
  * Copyright (c) 2018-2025 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
@@ -14,7 +14,7 @@
  *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
  *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
  *     Glenn Randers-Pehrson
- *   libpng versions 1.6.36, December 2018, through 1.6.46, January 2025:
+ *   libpng versions 1.6.36, December 2018, through 1.6.47, February 2025:
  *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
@@ -238,7 +238,7 @@
  *    ...
  *    1.5.30                  15    10530  15.so.15.30[.0]
  *    ...
- *    1.6.46                  16    10646  16.so.16.46[.0]
+ *    1.6.47                  16    10647  16.so.16.47[.0]
  *
  *    Henceforth the source version will match the shared-library major and
  *    minor numbers; the shared-library major version number will be used for
@@ -274,7 +274,7 @@
  */
 
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.46"
+#define PNG_LIBPNG_VER_STRING "1.6.47"
 #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
 
 /* The versions of shared library builds should stay in sync, going forward */
@@ -285,7 +285,7 @@
 /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
 #define PNG_LIBPNG_VER_MAJOR   1
 #define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 46
+#define PNG_LIBPNG_VER_RELEASE 47
 
 /* This should be zero for a public release, or non-zero for a
  * development version.
@@ -316,7 +316,7 @@
  * From version 1.0.1 it is:
  * XXYYZZ, where XX=major, YY=minor, ZZ=release
  */
-#define PNG_LIBPNG_VER 10646 /* 1.6.46 */
+#define PNG_LIBPNG_VER 10647 /* 1.6.47 */
 
 /* Library configuration: these options cannot be changed after
  * the library has been built.
@@ -426,7 +426,7 @@ extern "C" {
 /* This triggers a compiler error in png.c, if png.c and png.h
  * do not agree upon the version number.
  */
-typedef char* png_libpng_version_1_6_46;
+typedef char* png_libpng_version_1_6_47;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
diff --git a/source/libs/libpng/libpng-src/pngconf.h b/source/libs/libpng/libpng-src/pngconf.h
index 6bc46bcfc..42fa973c2 100644
--- a/source/libs/libpng/libpng-src/pngconf.h
+++ b/source/libs/libpng/libpng-src/pngconf.h
@@ -1,6 +1,6 @@
 /* pngconf.h - machine-configurable file for libpng
  *
- * libpng version 1.6.46
+ * libpng version 1.6.47
  *
  * Copyright (c) 2018-2025 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
diff --git a/source/libs/libpng/libpng-src/pngerror.c b/source/libs/libpng/libpng-src/pngerror.c
index aa0ae58e1..275b188d0 100644
--- a/source/libs/libpng/libpng-src/pngerror.c
+++ b/source/libs/libpng/libpng-src/pngerror.c
@@ -935,23 +935,37 @@ png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)
 int /* PRIVATE */
 png_safe_execute(png_imagep image, int (*function)(png_voidp), png_voidp arg)
 {
-   png_voidp saved_error_buf = image->opaque->error_buf;
+   const png_voidp saved_error_buf = image->opaque->error_buf;
    jmp_buf safe_jmpbuf;
-   int result;
 
    /* Safely execute function(arg), with png_error returning back here. */
    if (setjmp(safe_jmpbuf) == 0)
    {
+      int result;
+
       image->opaque->error_buf = safe_jmpbuf;
       result = function(arg);
       image->opaque->error_buf = saved_error_buf;
-      return result;
+
+      if (result)
+         return 1; /* success */
    }
 
-   /* On png_error, return via longjmp, pop the jmpbuf, and free the image. */
+   /* The function failed either because of a caught png_error and a regular
+    * return of false above or because of an uncaught png_error from the
+    * function itself.  Ensure that the error_buf is always set back to the
+    * value saved above:
+    */
    image->opaque->error_buf = saved_error_buf;
-   png_image_free(image);
-   return 0;
+
+   /* On the final false return, when about to return control to the caller, the
+    * image is freed (png_image_free does this check but it is duplicated here
+    * for clarity:
+    */
+   if (saved_error_buf == NULL)
+      png_image_free(image);
+
+   return 0; /* failure */
 }
 #endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */
 #endif /* READ || WRITE */
diff --git a/source/libs/libpng/libpng-src/pngget.c b/source/libs/libpng/libpng-src/pngget.c
index 2c646aee1..3623c5c7c 100644
--- a/source/libs/libpng/libpng-src/pngget.c
+++ b/source/libs/libpng/libpng-src/pngget.c
@@ -380,7 +380,13 @@ png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns)
     * Notice that this can overflow - a warning is output and 0 is
     * returned.
     */
-   return png_muldiv_warn(png_ptr, microns, 500, 127);
+   png_fixed_point result;
+
+   if (png_muldiv(&result, microns, 500, 127) != 0)
+      return result;
+
+   png_warning(png_ptr, "fixed point overflow ignored");
+   return 0;
 }
 
 png_fixed_point PNGAPI
@@ -390,7 +396,7 @@ png_get_x_offset_inches_fixed(png_const_structrp png_ptr,
    return png_fixed_inches_from_microns(png_ptr,
        png_get_x_offset_microns(png_ptr, info_ptr));
 }
-#endif
+#endif /* FIXED_POINT */
 
 #ifdef PNG_FIXED_POINT_SUPPORTED
 png_fixed_point PNGAPI
@@ -518,44 +524,31 @@ png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
 #  ifdef PNG_FLOATING_POINT_SUPPORTED
 png_uint_32 PNGAPI
 png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    double *white_x, double *white_y, double *red_x, double *red_y,
-    double *green_x, double *green_y, double *blue_x, double *blue_y)
+    double *whitex, double *whitey, double *redx, double *redy,
+    double *greenx, double *greeny, double *bluex, double *bluey)
 {
    png_debug1(1, "in %s retrieval function", "cHRM");
 
-   /* Quiet API change: this code used to only return the end points if a cHRM
-    * chunk was present, but the end points can also come from iCCP or sRGB
-    * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and
-    * the png_set_ APIs merely check that set end points are mutually
-    * consistent.
-    */
+   /* PNGv3: this just returns the values store from the cHRM, if any. */
    if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+       (info_ptr->valid & PNG_INFO_cHRM) != 0)
    {
-      if (white_x != NULL)
-         *white_x = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
-      if (white_y != NULL)
-         *white_y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");
-      if (red_x != NULL)
-         *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx,
-             "cHRM red X");
-      if (red_y != NULL)
-         *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy,
-             "cHRM red Y");
-      if (green_x != NULL)
-         *green_x = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");
-      if (green_y != NULL)
-         *green_y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");
-      if (blue_x != NULL)
-         *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex,
-             "cHRM blue X");
-      if (blue_y != NULL)
-         *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey,
-             "cHRM blue Y");
+      if (whitex != NULL)
+         *whitex = png_float(png_ptr, info_ptr->cHRM.whitex, "cHRM wx");
+      if (whitey != NULL)
+         *whitey = png_float(png_ptr, info_ptr->cHRM.whitey, "cHRM wy");
+      if (redx   != NULL)
+         *redx   = png_float(png_ptr, info_ptr->cHRM.redx,   "cHRM rx");
+      if (redy   != NULL)
+         *redy   = png_float(png_ptr, info_ptr->cHRM.redy,   "cHRM ry");
+      if (greenx != NULL)
+         *greenx = png_float(png_ptr, info_ptr->cHRM.greenx, "cHRM gx");
+      if (greeny != NULL)
+         *greeny = png_float(png_ptr, info_ptr->cHRM.greeny, "cHRM gy");
+      if (bluex  != NULL)
+         *bluex  = png_float(png_ptr, info_ptr->cHRM.bluex,  "cHRM bx");
+      if (bluey  != NULL)
+         *bluey  = png_float(png_ptr, info_ptr->cHRM.bluey,  "cHRM by");
       return PNG_INFO_cHRM;
    }
 
@@ -568,38 +561,31 @@ png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,
     double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
     double *blue_Z)
 {
+   png_XYZ XYZ;
    png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");
 
    if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+       (info_ptr->valid & PNG_INFO_cHRM) != 0 &&
+       png_XYZ_from_xy(&XYZ, &info_ptr->cHRM) == 0)
    {
       if (red_X != NULL)
-         *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X,
-             "cHRM red X");
+         *red_X = png_float(png_ptr, XYZ.red_X, "cHRM red X");
       if (red_Y != NULL)
-         *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y,
-             "cHRM red Y");
+         *red_Y = png_float(png_ptr, XYZ.red_Y, "cHRM red Y");
       if (red_Z != NULL)
-         *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z,
-             "cHRM red Z");
+         *red_Z = png_float(png_ptr, XYZ.red_Z, "cHRM red Z");
       if (green_X != NULL)
-         *green_X = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");
+         *green_X = png_float(png_ptr, XYZ.green_X, "cHRM green X");
       if (green_Y != NULL)
-         *green_Y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");
+         *green_Y = png_float(png_ptr, XYZ.green_Y, "cHRM green Y");
       if (green_Z != NULL)
-         *green_Z = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");
+         *green_Z = png_float(png_ptr, XYZ.green_Z, "cHRM green Z");
       if (blue_X != NULL)
-         *blue_X = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");
+         *blue_X = png_float(png_ptr, XYZ.blue_X, "cHRM blue X");
       if (blue_Y != NULL)
-         *blue_Y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");
+         *blue_Y = png_float(png_ptr, XYZ.blue_Y, "cHRM blue Y");
       if (blue_Z != NULL)
-         *blue_Z = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
+         *blue_Z = png_float(png_ptr, XYZ.blue_Z, "cHRM blue Z");
       return PNG_INFO_cHRM;
    }
 
@@ -616,29 +602,22 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
     png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
     png_fixed_point *int_blue_Z)
 {
+   png_XYZ XYZ;
    png_debug1(1, "in %s retrieval function", "cHRM_XYZ");
 
    if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+       (info_ptr->valid & PNG_INFO_cHRM) != 0U &&
+       png_XYZ_from_xy(&XYZ, &info_ptr->cHRM) == 0)
    {
-      if (int_red_X != NULL)
-         *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X;
-      if (int_red_Y != NULL)
-         *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y;
-      if (int_red_Z != NULL)
-         *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z;
-      if (int_green_X != NULL)
-         *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X;
-      if (int_green_Y != NULL)
-         *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y;
-      if (int_green_Z != NULL)
-         *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z;
-      if (int_blue_X != NULL)
-         *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X;
-      if (int_blue_Y != NULL)
-         *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y;
-      if (int_blue_Z != NULL)
-         *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z;
+      if (int_red_X != NULL) *int_red_X = XYZ.red_X;
+      if (int_red_Y != NULL) *int_red_Y = XYZ.red_Y;
+      if (int_red_Z != NULL) *int_red_Z = XYZ.red_Z;
+      if (int_green_X != NULL) *int_green_X = XYZ.green_X;
+      if (int_green_Y != NULL) *int_green_Y = XYZ.green_Y;
+      if (int_green_Z != NULL) *int_green_Z = XYZ.green_Z;
+      if (int_blue_X != NULL) *int_blue_X = XYZ.blue_X;
+      if (int_blue_Y != NULL) *int_blue_Y = XYZ.blue_Y;
+      if (int_blue_Z != NULL) *int_blue_Z = XYZ.blue_Z;
       return PNG_INFO_cHRM;
    }
 
@@ -647,31 +626,24 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
 
 png_uint_32 PNGAPI
 png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x,
-    png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y,
-    png_fixed_point *blue_x, png_fixed_point *blue_y)
+    png_fixed_point *whitex, png_fixed_point *whitey, png_fixed_point *redx,
+    png_fixed_point *redy, png_fixed_point *greenx, png_fixed_point *greeny,
+    png_fixed_point *bluex, png_fixed_point *bluey)
 {
    png_debug1(1, "in %s retrieval function", "cHRM");
 
+   /* PNGv3: this just returns the values store from the cHRM, if any. */
    if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+       (info_ptr->valid & PNG_INFO_cHRM) != 0)
    {
-      if (white_x != NULL)
-         *white_x = info_ptr->colorspace.end_points_xy.whitex;
-      if (white_y != NULL)
-         *white_y = info_ptr->colorspace.end_points_xy.whitey;
-      if (red_x != NULL)
-         *red_x = info_ptr->colorspace.end_points_xy.redx;
-      if (red_y != NULL)
-         *red_y = info_ptr->colorspace.end_points_xy.redy;
-      if (green_x != NULL)
-         *green_x = info_ptr->colorspace.end_points_xy.greenx;
-      if (green_y != NULL)
-         *green_y = info_ptr->colorspace.end_points_xy.greeny;
-      if (blue_x != NULL)
-         *blue_x = info_ptr->colorspace.end_points_xy.bluex;
-      if (blue_y != NULL)
-         *blue_y = info_ptr->colorspace.end_points_xy.bluey;
+      if (whitex != NULL) *whitex = info_ptr->cHRM.whitex;
+      if (whitey != NULL) *whitey = info_ptr->cHRM.whitey;
+      if (redx   != NULL) *redx   = info_ptr->cHRM.redx;
+      if (redy   != NULL) *redy   = info_ptr->cHRM.redy;
+      if (greenx != NULL) *greenx = info_ptr->cHRM.greenx;
+      if (greeny != NULL) *greeny = info_ptr->cHRM.greeny;
+      if (bluex  != NULL) *bluex  = info_ptr->cHRM.bluex;
+      if (bluey  != NULL) *bluey  = info_ptr->cHRM.bluey;
       return PNG_INFO_cHRM;
    }
 
@@ -688,11 +660,11 @@ png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
 {
    png_debug1(1, "in %s retrieval function", "gAMA");
 
+   /* PNGv3 compatibility: only report gAMA if it is really present. */
    if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-       file_gamma != NULL)
+       (info_ptr->valid & PNG_INFO_gAMA) != 0)
    {
-      *file_gamma = info_ptr->colorspace.gamma;
+      if (file_gamma != NULL) *file_gamma = info_ptr->gamma;
       return PNG_INFO_gAMA;
    }
 
@@ -707,12 +679,13 @@ png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr,
 {
    png_debug1(1, "in %s retrieval function", "gAMA(float)");
 
+   /* PNGv3 compatibility: only report gAMA if it is really present. */
    if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-      file_gamma != NULL)
+       (info_ptr->valid & PNG_INFO_gAMA) != 0)
    {
-      *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma,
-          "png_get_gAMA");
+      if (file_gamma != NULL)
+         *file_gamma = png_float(png_ptr, info_ptr->gamma, "gAMA");
+
       return PNG_INFO_gAMA;
    }
 
@@ -729,9 +702,10 @@ png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr,
    png_debug1(1, "in %s retrieval function", "sRGB");
 
    if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL)
+      (info_ptr->valid & PNG_INFO_sRGB) != 0)
    {
-      *file_srgb_intent = info_ptr->colorspace.rendering_intent;
+      if (file_srgb_intent != NULL)
+         *file_srgb_intent = info_ptr->rendering_intent;
       return PNG_INFO_sRGB;
    }
 
diff --git a/source/libs/libpng/libpng-src/pnginfo.h b/source/libs/libpng/libpng-src/pnginfo.h
index 4e55da397..c2a907bc5 100644
--- a/source/libs/libpng/libpng-src/pnginfo.h
+++ b/source/libs/libpng/libpng-src/pnginfo.h
@@ -86,20 +86,6 @@ struct png_info_def
     * and initialize the appropriate fields below.
     */
 
-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
-   /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are
-    * defined.  When COLORSPACE is switched on all the colorspace-defining
-    * chunks should be enabled, when GAMMA is switched on all the gamma-defining
-    * chunks should be enabled.  If this is not done it becomes possible to read
-    * inconsistent PNG files and assign a probably incorrect interpretation to
-    * the information.  (In other words, by carefully choosing which chunks to
-    * recognize the system configuration can select an interpretation for PNG
-    * files containing ambiguous data and this will result in inconsistent
-    * behavior between different libpng builds!)
-    */
-   png_colorspace colorspace;
-#endif
-
 #ifdef PNG_cICP_SUPPORTED
    /* cICP chunk data */
    png_byte cicp_colour_primaries;
@@ -211,11 +197,8 @@ defined(PNG_READ_BACKGROUND_SUPPORTED)
 #endif
 
 #ifdef PNG_eXIf_SUPPORTED
-   int num_exif;  /* Added at libpng-1.6.31 */
+   png_uint_32 num_exif;  /* Added at libpng-1.6.31 */
    png_bytep exif;
-# ifdef PNG_READ_eXIf_SUPPORTED
-   png_bytep eXIf_buf;  /* Added at libpng-1.6.32 */
-# endif
 #endif
 
 #ifdef PNG_hIST_SUPPORTED
@@ -288,5 +271,16 @@ defined(PNG_READ_BACKGROUND_SUPPORTED)
    png_bytepp row_pointers;        /* the image bits */
 #endif
 
+#ifdef PNG_cHRM_SUPPORTED
+   png_xy cHRM;
+#endif
+
+#ifdef PNG_gAMA_SUPPORTED
+   png_fixed_point gamma;
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+   int rendering_intent;
+#endif
 };
 #endif /* PNGINFO_H */
diff --git a/source/libs/libpng/libpng-src/pnglibconf.h b/source/libs/libpng/libpng-src/pnglibconf.h
index b7f6928a0..748220bfc 100644
--- a/source/libs/libpng/libpng-src/pnglibconf.h
+++ b/source/libs/libpng/libpng-src/pnglibconf.h
@@ -1,6 +1,6 @@
 /* pnglibconf.h - library build configuration */
 
-/* libpng version 1.6.46 */
+/* libpng version 1.6.47 */
 
 /* Copyright (c) 2018-2025 Cosmin Truta */
 /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
diff --git a/source/libs/libpng/libpng-src/pngmem.c b/source/libs/libpng/libpng-src/pngmem.c
index d391c13ff..90c13b106 100644
--- a/source/libs/libpng/libpng-src/pngmem.c
+++ b/source/libs/libpng/libpng-src/pngmem.c
@@ -72,30 +72,29 @@ png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
     * to implement a user memory handler.  This checks to be sure it isn't
     * called with big numbers.
     */
-#ifndef PNG_USER_MEM_SUPPORTED
-   PNG_UNUSED(png_ptr)
-#endif
+#  ifdef PNG_MAX_MALLOC_64K
+      /* This is support for legacy systems which had segmented addressing
+       * limiting the maximum allocation size to 65536.  It takes precedence
+       * over PNG_SIZE_MAX which is set to 65535 on true 16-bit systems.
+       *
+       * TODO: libpng-1.8: finally remove both cases.
+       */
+      if (size > 65536U) return NULL;
+#  endif
 
-   /* Some compilers complain that this is always true.  However, it
-    * can be false when integer overflow happens.
+   /* This is checked too because the system malloc call below takes a (size_t).
     */
-   if (size > 0 && size <= PNG_SIZE_MAX
-#     ifdef PNG_MAX_MALLOC_64K
-         && size <= 65536U
-#     endif
-      )
-   {
-#ifdef PNG_USER_MEM_SUPPORTED
+   if (size > PNG_SIZE_MAX) return NULL;
+
+#  ifdef PNG_USER_MEM_SUPPORTED
       if (png_ptr != NULL && png_ptr->malloc_fn != NULL)
          return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);
+#  else
+      PNG_UNUSED(png_ptr)
+#  endif
 
-      else
-#endif
-         return malloc((size_t)size); /* checked for truncation above */
-   }
-
-   else
-      return NULL;
+   /* Use the system malloc */
+   return malloc((size_t)/*SAFE*/size); /* checked for truncation above */
 }
 
 #if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
diff --git a/source/libs/libpng/libpng-src/pngpread.c b/source/libs/libpng/libpng-src/pngpread.c
index d67a5676f..60d810693 100644
--- a/source/libs/libpng/libpng-src/pngpread.c
+++ b/source/libs/libpng/libpng-src/pngpread.c
@@ -193,17 +193,8 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
     */
    if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0)
    {
-      png_byte chunk_length[4];
-      png_byte chunk_tag[4];
-
       PNG_PUSH_SAVE_BUFFER_IF_LT(8)
-      png_push_fill_buffer(png_ptr, chunk_length, 4);
-      png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
-      png_reset_crc(png_ptr);
-      png_crc_read(png_ptr, chunk_tag, 4);
-      png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
-      png_check_chunk_name(png_ptr, png_ptr->chunk_name);
-      png_check_chunk_length(png_ptr, png_ptr->push_length);
+      png_ptr->push_length = png_read_chunk_header(png_ptr);
       png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
    }
 
@@ -244,13 +235,13 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
          png_error(png_ptr, "Invalid IHDR length");
 
       PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
+      png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length);
    }
 
    else if (chunk_name == png_IEND)
    {
       PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
+      png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length);
 
       png_ptr->process_mode = PNG_READ_DONE_MODE;
       png_push_have_end(png_ptr, info_ptr);
@@ -267,12 +258,6 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
    }
 #endif
 
-   else if (chunk_name == png_PLTE)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
    else if (chunk_name == png_IDAT)
    {
       png_ptr->idat_size = png_ptr->push_length;
@@ -285,179 +270,10 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
       return;
    }
 
-#ifdef PNG_READ_gAMA_SUPPORTED
-   else if (png_ptr->chunk_name == png_gAMA)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sBIT_SUPPORTED
-   else if (png_ptr->chunk_name == png_sBIT)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_cHRM_SUPPORTED
-   else if (png_ptr->chunk_name == png_cHRM)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_cICP_SUPPORTED
-   else if (png_ptr->chunk_name == png_cICP)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_cICP(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_cLLI_SUPPORTED
-   else if (png_ptr->chunk_name == png_cLLI)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_cLLI(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_mDCV_SUPPORTED
-   else if (png_ptr->chunk_name == png_mDCV)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_mDCV(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_eXIf_SUPPORTED
-   else if (png_ptr->chunk_name == png_eXIf)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_eXIf(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sRGB_SUPPORTED
-   else if (chunk_name == png_sRGB)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_iCCP_SUPPORTED
-   else if (png_ptr->chunk_name == png_iCCP)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sPLT_SUPPORTED
-   else if (chunk_name == png_sPLT)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_tRNS_SUPPORTED
-   else if (chunk_name == png_tRNS)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_bKGD_SUPPORTED
-   else if (chunk_name == png_bKGD)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_hIST_SUPPORTED
-   else if (chunk_name == png_hIST)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_pHYs_SUPPORTED
-   else if (chunk_name == png_pHYs)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_oFFs_SUPPORTED
-   else if (chunk_name == png_oFFs)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
-   }
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-   else if (chunk_name == png_pCAL)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sCAL_SUPPORTED
-   else if (chunk_name == png_sCAL)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_tIME_SUPPORTED
-   else if (chunk_name == png_tIME)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_tEXt_SUPPORTED
-   else if (chunk_name == png_tEXt)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_zTXt_SUPPORTED
-   else if (chunk_name == png_zTXt)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_iTXt_SUPPORTED
-   else if (chunk_name == png_iTXt)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
-   }
-#endif
-
    else
    {
       PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length,
-          PNG_HANDLE_CHUNK_AS_DEFAULT);
+      png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length);
    }
 
    png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
diff --git a/source/libs/libpng/libpng-src/pngpriv.h b/source/libs/libpng/libpng-src/pngpriv.h
index aeda39f0a..d514dff5c 100644
--- a/source/libs/libpng/libpng-src/pngpriv.h
+++ b/source/libs/libpng/libpng-src/pngpriv.h
@@ -671,7 +671,7 @@
 #define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200U
 #define PNG_FLAG_CRC_CRITICAL_USE         0x0400U
 #define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800U
-#define PNG_FLAG_ASSUME_sRGB              0x1000U /* Added to libpng-1.5.4 */
+/*      PNG_FLAG_ASSUME_sRGB unused       0x1000U  * Added to libpng-1.5.4 */
 #define PNG_FLAG_OPTIMIZE_ALPHA           0x2000U /* Added to libpng-1.5.4 */
 #define PNG_FLAG_DETECT_UNINITIALIZED     0x4000U /* Added to libpng-1.5.4 */
 /* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000U */
@@ -802,11 +802,31 @@
  *
  * PNG_32b correctly produces a value shifted by up to 24 bits, even on
  * architectures where (int) is only 16 bits.
+ *
+ * 1.6.47: PNG_32b was made into a preprocessor evaluable macro by replacing the
+ * static_cast with a promoting binary operation using a guaranteed 32-bit
+ * (minimum) unsigned value.
  */
-#define PNG_32b(b,s) ((png_uint_32)(b) << (s))
+#define PNG_32b(b,s) (((0xFFFFFFFFU)&(b)) << (s))
 #define PNG_U32(b1,b2,b3,b4) \
    (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))
 
+/* Chunk name validation.  When using these macros all the arguments should be
+ * constants, otherwise code bloat may well occur.  The macros are provided
+ * primarily for use in #if checks.
+ *
+ * PNG_32to8 produces a byte value with the right shift; used to extract the
+ * byte value from a chunk name.
+ */
+#define PNG_32to8(cn,s) (((cn) >> (s)) & 0xffU)
+#define PNG_CN_VALID_UPPER(b) ((b) >= 65 && (b) <= 90) /* upper-case ASCII */
+#define PNG_CN_VALID_ASCII(b) PNG_CN_VALID_UPPER((b) & ~32U)
+#define PNG_CHUNK_NAME_VALID(cn) (\
+   PNG_CN_VALID_ASCII(PNG_32to8(cn,24)) && /* critical, !ancillary */\
+   PNG_CN_VALID_ASCII(PNG_32to8(cn,16)) && /* public, !privately defined */\
+   PNG_CN_VALID_UPPER(PNG_32to8(cn, 8)) && /* VALID, !reserved */\
+   PNG_CN_VALID_ASCII(PNG_32to8(cn, 0))   /* data-dependent, !copy ok */)
+
 /* Constants for known chunk types.
  *
  * MAINTAINERS: If you need to add a chunk, define the name here.
@@ -834,11 +854,14 @@
 #define png_IEND PNG_U32( 73,  69,  78,  68)
 #define png_IHDR PNG_U32( 73,  72,  68,  82)
 #define png_PLTE PNG_U32( 80,  76,  84,  69)
+#define png_acTL PNG_U32( 97,  99,  84,  76) /* PNGv3: APNG */
 #define png_bKGD PNG_U32( 98,  75,  71,  68)
 #define png_cHRM PNG_U32( 99,  72,  82,  77)
 #define png_cICP PNG_U32( 99,  73,  67,  80) /* PNGv3 */
 #define png_cLLI PNG_U32( 99,  76,  76,  73) /* PNGv3 */
 #define png_eXIf PNG_U32(101,  88,  73, 102) /* registered July 2017 */
+#define png_fcTL PNG_U32(102,  99,  84,  76) /* PNGv3: APNG */
+#define png_fdAT PNG_U32(102, 100,  65,  84) /* PNGv3: APNG */
 #define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
 #define png_gAMA PNG_U32(103,  65,  77,  65)
 #define png_gIFg PNG_U32(103,  73,  70, 103)
@@ -888,11 +911,74 @@
 #define PNG_CHUNK_RESERVED(c)     (1 & ((c) >> 13))
 #define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >>  5))
 
+/* Known chunks.  All supported chunks must be listed here.  The macro PNG_CHUNK
+ * contains the four character ASCII name by which the chunk is identified.  The
+ * macro is implemented as required to build tables or switch statements which
+ * require entries for every known chunk.  The macro also contains an index
+ * value which should be in order (this is checked in png.c).
+ *
+ * Notice that "known" does not require "SUPPORTED"; tables should be built in
+ * such a way that chunks unsupported in a build require no more than the table
+ * entry (which should be small.)  In particular function pointers for
+ * unsupported chunks should be NULL.
+ *
+ * At present these index values are not exported (not part of the public API)
+ * so can be changed at will.  For convenience the names are in lexical sort
+ * order but with the critical chunks at the start in the order of occurence in
+ * a PNG.
+ *
+ * PNG_INFO_ values do not exist for every one of these chunk handles; for
+ * example PNG_INFO_{IDAT,IEND,tEXt,iTXt,zTXt} and possibly other chunks in the
+ * future.
+ */
+#define PNG_KNOWN_CHUNKS\
+   PNG_CHUNK(IHDR,  0)\
+   PNG_CHUNK(PLTE,  1)\
+   PNG_CHUNK(IDAT,  2)\
+   PNG_CHUNK(IEND,  3)\
+   PNG_CHUNK(acTL,  4)\
+   PNG_CHUNK(bKGD,  5)\
+   PNG_CHUNK(cHRM,  6)\
+   PNG_CHUNK(cICP,  7)\
+   PNG_CHUNK(cLLI,  8)\
+   PNG_CHUNK(eXIf,  9)\
+   PNG_CHUNK(fcTL, 10)\
+   PNG_CHUNK(fdAT, 11)\
+   PNG_CHUNK(gAMA, 12)\
+   PNG_CHUNK(hIST, 13)\
+   PNG_CHUNK(iCCP, 14)\
+   PNG_CHUNK(iTXt, 15)\
+   PNG_CHUNK(mDCV, 16)\
+   PNG_CHUNK(oFFs, 17)\
+   PNG_CHUNK(pCAL, 18)\
+   PNG_CHUNK(pHYs, 19)\
+   PNG_CHUNK(sBIT, 20)\
+   PNG_CHUNK(sCAL, 21)\
+   PNG_CHUNK(sPLT, 22)\
+   PNG_CHUNK(sRGB, 23)\
+   PNG_CHUNK(tEXt, 24)\
+   PNG_CHUNK(tIME, 25)\
+   PNG_CHUNK(tRNS, 26)\
+   PNG_CHUNK(zTXt, 27)
+
 /* Gamma values (new at libpng-1.5.4): */
 #define PNG_GAMMA_MAC_OLD 151724  /* Assume '1.8' is really 2.2/1.45! */
 #define PNG_GAMMA_MAC_INVERSE 65909
 #define PNG_GAMMA_sRGB_INVERSE 45455
 
+/* gamma sanity check.  libpng cannot implement gamma transforms outside a
+ * certain limit because of its use of 16-bit fixed point intermediate values.
+ * Gamma values that are too large or too small will zap the 16-bit values all
+ * to 0 or 65535 resulting in an obvious 'bad' image.
+ *
+ * In libpng 1.6.0 the limits were changed from 0.07..3 to 0.01..100 to
+ * accommodate the optimal 16-bit gamma of 36 and its reciprocal.
+ *
+ * These are png_fixed_point integral values:
+ */
+#define PNG_LIB_GAMMA_MIN 1000
+#define PNG_LIB_GAMMA_MAX 10000000
+
 /* Almost everything below is C specific; the #defines above can be used in
  * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot.
  */
@@ -956,7 +1042,6 @@ extern "C" {
  *
  * All of these functions must be declared with PNG_INTERNAL_FUNCTION.
  */
-
 /* Zlib support */
 #define PNG_UNEXPECTED_ZLIB_RETURN (-7)
 PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
@@ -996,6 +1081,25 @@ PNG_INTERNAL_FUNCTION(png_uint_32,png_fixed_ITU,(png_const_structrp png_ptr,
 PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
    png_const_charp user_png_ver),PNG_EMPTY);
 
+#ifdef PNG_READ_SUPPORTED /* should only be used on read */
+/* Security: read limits on the largest allocations while reading a PNG.  This
+ * avoids very large allocations caused by PNG files with damaged or altered
+ * chunk 'length' fields.
+ */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED /* run-time limit */
+#  define png_chunk_max(png_ptr) ((png_ptr)->user_chunk_malloc_max)
+
+#elif PNG_USER_CHUNK_MALLOC_MAX > 0 /* compile-time limit */
+#  define png_chunk_max(png_ptr) ((void)png_ptr, PNG_USER_CHUNK_MALLOC_MAX)
+
+#elif (defined PNG_MAX_MALLOC_64K)  /* legacy system limit */
+#  define png_chunk_max(png_ptr) ((void)png_ptr, 65536U)
+
+#else                               /* modern system limit SIZE_MAX (C99) */
+#  define png_chunk_max(png_ptr) ((void)png_ptr, PNG_SIZE_MAX)
+#endif
+#endif /* READ */
+
 /* Internal base allocator - no messages, NULL on failure to allocate.  This
  * does, however, call the application provided allocator and that could call
  * png_error (although that would be a bug in the application implementation.)
@@ -1095,9 +1199,6 @@ PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,
 PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,
    png_uint_32 skip),PNG_EMPTY);
 
-/* Read the CRC from the file and compare it to the libpng calculated CRC */
-PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY);
-
 /* Calculate the CRC over a section of data.  Note that we are only
  * passing a maximum of 64K on systems that have this as a memory limit,
  * since this is the maximum buffer size we can specify.
@@ -1175,10 +1276,10 @@ PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr,
 
 #ifdef PNG_WRITE_iCCP_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
-   png_const_charp name, png_const_bytep profile), PNG_EMPTY);
-   /* The profile must have been previously validated for correctness, the
-    * length comes from the first four bytes.  Only the base, deflate,
-    * compression is supported.
+   png_const_charp name, png_const_bytep profile, png_uint_32 proflen),
+   PNG_EMPTY);
+   /* Writes a previously 'set' profile.  The profile argument is **not**
+    * compressed.
     */
 #endif
 
@@ -1487,134 +1588,36 @@ PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info,
 /* The following decodes the appropriate chunks, and does error correction,
  * then calls the appropriate callback for the chunk if it is valid.
  */
-
-/* Decode the IHDR chunk */
-PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_cICP_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_cICP,(png_structrp png_ptr,
-        png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_cLLI_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_cLLI,(png_structrp png_ptr,
-        png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_eXIf_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif /* READ_iCCP */
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_mDCV_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_mDCV,(png_structrp png_ptr,
-        png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif /* READ_sPLT */
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr,
-    png_uint_32 chunk_name),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr,
-    png_uint_32 chunk_length),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY);
+typedef enum
+{
+   /* Result of a call to png_handle_chunk made to handle the current chunk
+    * png_struct::chunk_name on read.  Always informational, either the stream
+    * is read for the next chunk or the routine will call png_error.
+    *
+    * NOTE: order is important internally.  handled_saved and above are regarded
+    * as handling the chunk.
+    */
+   handled_error = 0,  /* bad crc or known and bad format or too long */
+   handled_discarded,  /* not saved in the unknown chunk list */
+   handled_saved,      /* saved in the unknown chunk list */
+   handled_ok          /* known, supported and handled without error */
+} png_handle_result_code;
+
+PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_unknown,
+    (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep),
+    PNG_EMPTY);
    /* This is the function that gets called for unknown chunks.  The 'keep'
     * argument is either non-zero for a known chunk that has been set to be
     * handled as unknown or zero for an unknown chunk.  By default the function
     * just skips the chunk or errors out if it is critical.
     */
 
+PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_chunk,
+    (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+   /* This handles the current chunk png_ptr->chunk_name with unread
+    * data[length] and returns one of the above result codes.
+    */
+
 #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
     defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
 PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
@@ -1654,8 +1657,6 @@ PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,
     png_bytep buffer, size_t buffer_length),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),
     PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr,
-   png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr,
    png_inforp info_ptr),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,
@@ -1668,109 +1669,28 @@ PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,
     png_inforp info_ptr),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr),
     PNG_EMPTY);
-#  ifdef PNG_READ_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-#  endif
-#  ifdef PNG_READ_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-#  endif
-#  ifdef PNG_READ_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-#  endif
-
 #endif /* PROGRESSIVE_READ */
 
-/* Added at libpng version 1.6.0 */
-#ifdef PNG_GAMMA_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY);
-   /* Set the colorspace gamma with a value provided by the application or by
-    * the gAMA chunk on read.  The value will override anything set by an ICC
-    * profile.
-    */
-
-PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr,
-    png_inforp info_ptr), PNG_EMPTY);
-   /* Synchronize the info 'valid' flags with the colorspace */
-
-PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr,
-    png_inforp info_ptr), PNG_EMPTY);
-   /* Copy the png_struct colorspace to the info_struct and call the above to
-    * synchronize the flags.  Checks for NULL info_ptr and does nothing.
-    */
-#endif
-
-/* Added at libpng version 1.4.0 */
-#ifdef PNG_COLORSPACE_SUPPORTED
-/* These internal functions are for maintaining the colorspace structure within
- * a png_info or png_struct (or, indeed, both).
- */
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities,
-   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy,
-    int preferred), PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints,
-   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ,
-    int preferred), PNG_EMPTY);
-
-#ifdef PNG_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, int intent), PNG_EMPTY);
-   /* This does set the colorspace gAMA and cHRM values too, but doesn't set the
-    * flags to write them, if it returns false there was a problem and an error
-    * message has already been output (but the colorspace may still need to be
-    * synced to record the invalid flag).
-    */
-#endif /* sRGB */
-
 #ifdef PNG_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length, png_const_bytep profile, int color_type),
-   PNG_EMPTY);
-   /* The 'name' is used for information only */
-
 /* Routines for checking parts of an ICC profile. */
 #ifdef PNG_READ_iCCP_SUPPORTED
 PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length), PNG_EMPTY);
+   png_const_charp name, png_uint_32 profile_length), PNG_EMPTY);
 #endif /* READ_iCCP */
 PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length,
+   png_const_charp name, png_uint_32 profile_length,
    png_const_bytep profile /* first 132 bytes only */, int color_type),
    PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length,
+   png_const_charp name, png_uint_32 profile_length,
    png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY);
-#ifdef PNG_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,(
-   png_const_structrp png_ptr, png_colorspacerp colorspace,
-   png_const_bytep profile, uLong adler), PNG_EMPTY);
-   /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may
-    * be zero to indicate that it is not available.  It is used, if provided,
-    * as a fast check on the profile when checking to see if it is sRGB.
-    */
-#endif
 #endif /* iCCP */
 
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients,
-   (png_structrp png_ptr), PNG_EMPTY);
-   /* Set the rgb_to_gray coefficients from the colorspace Y values */
+PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients, (png_structrp png_ptr),
+   PNG_EMPTY);
+   /* Set the rgb_to_gray coefficients from the cHRM Y values (if unset) */
 #endif /* READ_RGB_TO_GRAY */
-#endif /* COLORSPACE */
 
 /* Added at libpng version 1.4.0 */
 PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,
@@ -2032,8 +1952,10 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
    size_t size),PNG_EMPTY);
 #endif /* pCAL || sCAL */
 
-#if defined(PNG_GAMMA_SUPPORTED) ||\
-    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)
+#if defined(PNG_READ_GAMMA_SUPPORTED) ||\
+    defined(PNG_COLORSPACE_SUPPORTED) ||\
+    defined(PNG_INCH_CONVERSIONS_SUPPORTED) ||\
+    defined(PNG_READ_pHYs_SUPPORTED)
 /* Added at libpng version 1.5.0 */
 /* This is a utility to provide a*times/div (rounded) and indicate
  * if there is an overflow.  The result is a boolean - false (0)
@@ -2042,22 +1964,14 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
  */
 PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a,
    png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY);
-#endif
 
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
-/* Same deal, but issue a warning on overflow and return 0. */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn,
-   (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by,
-   png_int_32 divided_by),PNG_EMPTY);
-#endif
-
-#ifdef PNG_GAMMA_SUPPORTED
 /* Calculate a reciprocal - used for gamma values.  This returns
  * 0 if the argument is 0 in order to maintain an undefined value;
  * there are no warnings.
  */
 PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
    PNG_EMPTY);
+#endif
 
 #ifdef PNG_READ_GAMMA_SUPPORTED
 /* The same but gives a reciprocal of the product of two fixed point
@@ -2066,14 +1980,22 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
  */
 PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a,
    png_fixed_point b),PNG_EMPTY);
-#endif
 
 /* Return true if the gamma value is significantly different from 1.0 */
 PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
    PNG_EMPTY);
-#endif
 
-#ifdef PNG_READ_GAMMA_SUPPORTED
+/* PNGv3: 'resolve' the file gamma according to the new PNGv3 rules for colour
+ * space information.
+ *
+ * NOTE: this uses precisely those chunks that libpng supports.  For example it
+ * doesn't use iCCP and it can only use cICP for known and manageable
+ * transforms.  For this reason a gamma specified by png_set_gamma always takes
+ * precedence.
+ */
+PNG_INTERNAL_FUNCTION(png_fixed_point,png_resolve_file_gamma,
+   (png_const_structrp png_ptr),PNG_EMPTY);
+
 /* Internal fixed point gamma correction.  These APIs are called as
  * required to convert single values - they don't need to be fast,
  * they are not used when processing image pixel values.
@@ -2091,6 +2013,22 @@ PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr),
    PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr,
    int bit_depth),PNG_EMPTY);
+#endif /* READ_GAMMA */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+/* Set the RGB coefficients if not already set by png_set_rgb_to_gray */
+PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients,(png_structrp png_ptr),
+   PNG_EMPTY);
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED) || defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+PNG_INTERNAL_FUNCTION(int,png_XYZ_from_xy,(png_XYZ *XYZ, const png_xy *xy),
+   PNG_EMPTY);
+#endif /* cHRM || READ_RGB_TO_GRAY */
+
+#ifdef PNG_COLORSPACE_SUPPORTED
+PNG_INTERNAL_FUNCTION(int,png_xy_from_XYZ,(png_xy *xy, const png_XYZ *XYZ),
+   PNG_EMPTY);
 #endif
 
 /* SIMPLIFIED READ/WRITE SUPPORT */
diff --git a/source/libs/libpng/libpng-src/pngread.c b/source/libs/libpng/libpng-src/pngread.c
index 73192f75e..0fd364827 100644
--- a/source/libs/libpng/libpng-src/pngread.c
+++ b/source/libs/libpng/libpng-src/pngread.c
@@ -131,14 +131,11 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr)
          png_ptr->mode |= PNG_AFTER_IDAT;
       }
 
-      /* This should be a binary subdivision search or a hash for
-       * matching the chunk name rather than a linear search.
-       */
       if (chunk_name == png_IHDR)
-         png_handle_IHDR(png_ptr, info_ptr, length);
+         png_handle_chunk(png_ptr, info_ptr, length);
 
       else if (chunk_name == png_IEND)
-         png_handle_IEND(png_ptr, info_ptr, length);
+         png_handle_chunk(png_ptr, info_ptr, length);
 
 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
       else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
@@ -155,8 +152,6 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr)
          }
       }
 #endif
-      else if (chunk_name == png_PLTE)
-         png_handle_PLTE(png_ptr, info_ptr, length);
 
       else if (chunk_name == png_IDAT)
       {
@@ -164,114 +159,8 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr)
          break;
       }
 
-#ifdef PNG_READ_bKGD_SUPPORTED
-      else if (chunk_name == png_bKGD)
-         png_handle_bKGD(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-      else if (chunk_name == png_cHRM)
-         png_handle_cHRM(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cICP_SUPPORTED
-      else if (chunk_name == png_cICP)
-         png_handle_cICP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cLLI_SUPPORTED
-      else if (chunk_name == png_cLLI)
-         png_handle_cLLI(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_eXIf_SUPPORTED
-      else if (chunk_name == png_eXIf)
-         png_handle_eXIf(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-      else if (chunk_name == png_gAMA)
-         png_handle_gAMA(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-      else if (chunk_name == png_hIST)
-         png_handle_hIST(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_mDCV_SUPPORTED
-      else if (chunk_name == png_mDCV)
-         png_handle_mDCV(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-      else if (chunk_name == png_oFFs)
-         png_handle_oFFs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-      else if (chunk_name == png_pCAL)
-         png_handle_pCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-      else if (chunk_name == png_sCAL)
-         png_handle_sCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-      else if (chunk_name == png_pHYs)
-         png_handle_pHYs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-      else if (chunk_name == png_sBIT)
-         png_handle_sBIT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-      else if (chunk_name == png_sRGB)
-         png_handle_sRGB(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-      else if (chunk_name == png_iCCP)
-         png_handle_iCCP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-      else if (chunk_name == png_sPLT)
-         png_handle_sPLT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-      else if (chunk_name == png_tEXt)
-         png_handle_tEXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-      else if (chunk_name == png_tIME)
-         png_handle_tIME(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-      else if (chunk_name == png_tRNS)
-         png_handle_tRNS(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-      else if (chunk_name == png_zTXt)
-         png_handle_zTXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-      else if (chunk_name == png_iTXt)
-         png_handle_iTXt(png_ptr, info_ptr, length);
-#endif
-
       else
-         png_handle_unknown(png_ptr, info_ptr, length,
-             PNG_HANDLE_CHUNK_AS_DEFAULT);
+         png_handle_chunk(png_ptr, info_ptr, length);
    }
 }
 #endif /* SEQUENTIAL_READ */
@@ -816,10 +705,10 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr)
          png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
 
       if (chunk_name == png_IEND)
-         png_handle_IEND(png_ptr, info_ptr, length);
+         png_handle_chunk(png_ptr, info_ptr, length);
 
       else if (chunk_name == png_IHDR)
-         png_handle_IHDR(png_ptr, info_ptr, length);
+         png_handle_chunk(png_ptr, info_ptr, length);
 
       else if (info_ptr == NULL)
          png_crc_finish(png_ptr, length);
@@ -853,117 +742,9 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr)
 
          png_crc_finish(png_ptr, length);
       }
-      else if (chunk_name == png_PLTE)
-         png_handle_PLTE(png_ptr, info_ptr, length);
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-      else if (chunk_name == png_bKGD)
-         png_handle_bKGD(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-      else if (chunk_name == png_cHRM)
-         png_handle_cHRM(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cICP_SUPPORTED
-      else if (chunk_name == png_cICP)
-         png_handle_cICP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cLLI_SUPPORTED
-      else if (chunk_name == png_cLLI)
-         png_handle_cLLI(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_eXIf_SUPPORTED
-      else if (chunk_name == png_eXIf)
-         png_handle_eXIf(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-      else if (chunk_name == png_gAMA)
-         png_handle_gAMA(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-      else if (chunk_name == png_hIST)
-         png_handle_hIST(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_mDCV_SUPPORTED
-      else if (chunk_name == png_mDCV)
-         png_handle_mDCV(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-      else if (chunk_name == png_oFFs)
-         png_handle_oFFs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-      else if (chunk_name == png_pCAL)
-         png_handle_pCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-      else if (chunk_name == png_sCAL)
-         png_handle_sCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-      else if (chunk_name == png_pHYs)
-         png_handle_pHYs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-      else if (chunk_name == png_sBIT)
-         png_handle_sBIT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-      else if (chunk_name == png_sRGB)
-         png_handle_sRGB(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-      else if (chunk_name == png_iCCP)
-         png_handle_iCCP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-      else if (chunk_name == png_sPLT)
-         png_handle_sPLT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-      else if (chunk_name == png_tEXt)
-         png_handle_tEXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-      else if (chunk_name == png_tIME)
-         png_handle_tIME(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-      else if (chunk_name == png_tRNS)
-         png_handle_tRNS(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-      else if (chunk_name == png_zTXt)
-         png_handle_zTXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-      else if (chunk_name == png_iTXt)
-         png_handle_iTXt(png_ptr, info_ptr, length);
-#endif
 
       else
-         png_handle_unknown(png_ptr, info_ptr, length,
-             PNG_HANDLE_CHUNK_AS_DEFAULT);
+         png_handle_chunk(png_ptr, info_ptr, length);
    } while ((png_ptr->mode & PNG_HAVE_IEND) == 0);
 }
 #endif /* SEQUENTIAL_READ */
@@ -1414,6 +1195,31 @@ png_image_format(png_structrp png_ptr)
    return format;
 }
 
+static int
+chromaticities_match_sRGB(const png_xy *xy)
+{
+#  define sRGB_TOLERANCE 1000
+   static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */
+   {
+      /* color      x       y */
+      /* red   */ 64000, 33000,
+      /* green */ 30000, 60000,
+      /* blue  */ 15000,  6000,
+      /* white */ 31270, 32900
+   };
+
+   if (PNG_OUT_OF_RANGE(xy->whitex, sRGB_xy.whitex,sRGB_TOLERANCE) ||
+       PNG_OUT_OF_RANGE(xy->whitey, sRGB_xy.whitey,sRGB_TOLERANCE) ||
+       PNG_OUT_OF_RANGE(xy->redx,   sRGB_xy.redx,  sRGB_TOLERANCE) ||
+       PNG_OUT_OF_RANGE(xy->redy,   sRGB_xy.redy,  sRGB_TOLERANCE) ||
+       PNG_OUT_OF_RANGE(xy->greenx, sRGB_xy.greenx,sRGB_TOLERANCE) ||
+       PNG_OUT_OF_RANGE(xy->greeny, sRGB_xy.greeny,sRGB_TOLERANCE) ||
+       PNG_OUT_OF_RANGE(xy->bluex,  sRGB_xy.bluex, sRGB_TOLERANCE) ||
+       PNG_OUT_OF_RANGE(xy->bluey,  sRGB_xy.bluey, sRGB_TOLERANCE))
+      return 0;
+   return 1;
+}
+
 /* Is the given gamma significantly different from sRGB?  The test is the same
  * one used in pngrtran.c when deciding whether to do gamma correction.  The
  * arithmetic optimizes the division by using the fact that the inverse of the
@@ -1422,22 +1228,44 @@ png_image_format(png_structrp png_ptr)
 static int
 png_gamma_not_sRGB(png_fixed_point g)
 {
-   if (g < PNG_FP_1)
-   {
-      /* An uninitialized gamma is assumed to be sRGB for the simplified API. */
-      if (g == 0)
-         return 0;
-
-      return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */);
-   }
+   /* 1.6.47: use the same sanity checks as used in pngrtran.c */
+   if (g < PNG_LIB_GAMMA_MIN || g > PNG_LIB_GAMMA_MAX)
+      return 0; /* Includes the uninitialized value 0 */
 
-   return 1;
+   return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */);
 }
 
 /* Do the main body of a 'png_image_begin_read' function; read the PNG file
  * header and fill in all the information.  This is executed in a safe context,
  * unlike the init routine above.
  */
+static int
+png_image_is_not_sRGB(png_const_structrp png_ptr)
+{
+   /* Does the colorspace **not** match sRGB?  The flag is only set if the
+    * answer can be determined reliably.
+    *
+    * png_struct::chromaticities always exists since the simplified API
+    * requires rgb-to-gray.  The mDCV, cICP and cHRM chunks may all set it to
+    * a non-sRGB value, so it needs to be checked but **only** if one of
+    * those chunks occured in the file.
+    */
+   /* Highest priority: check to be safe. */
+   if (png_has_chunk(png_ptr, cICP) || png_has_chunk(png_ptr, mDCV))
+      return !chromaticities_match_sRGB(&png_ptr->chromaticities);
+
+   /* If the image is marked as sRGB then it is... */
+   if (png_has_chunk(png_ptr, sRGB))
+      return 0;
+
+   /* Last stop: cHRM, must check: */
+   if (png_has_chunk(png_ptr, cHRM))
+      return !chromaticities_match_sRGB(&png_ptr->chromaticities);
+
+   /* Else default to sRGB */
+   return 0;
+}
+
 static int
 png_image_read_header(png_voidp argument)
 {
@@ -1459,17 +1287,13 @@ png_image_read_header(png_voidp argument)
 
       image->format = format;
 
-#ifdef PNG_COLORSPACE_SUPPORTED
-      /* Does the colorspace match sRGB?  If there is no color endpoint
-       * (colorant) information assume yes, otherwise require the
-       * 'ENDPOINTS_MATCHP_sRGB' colorspace flag to have been set.  If the
-       * colorspace has been determined to be invalid ignore it.
+      /* Greyscale images don't (typically) have colour space information and
+       * using it is pretty much impossible, so use sRGB for grayscale (it
+       * doesn't matter r==g==b so the transform is irrelevant.)
        */
-      if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags
-         & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB|
-            PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS))
+      if ((format & PNG_FORMAT_FLAG_COLOR) != 0 &&
+          png_image_is_not_sRGB(png_ptr))
          image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB;
-#endif
    }
 
    /* We need the maximum number of entries regardless of the format the
@@ -1657,21 +1481,18 @@ png_image_skip_unused_chunks(png_structrp png_ptr)
     * potential vulnerability to security problems in the unused chunks.
     *
     * At present the iCCP chunk data isn't used, so iCCP chunk can be ignored
-    * too.  This allows the simplified API to be compiled without iCCP support,
-    * however if the support is there the chunk is still checked to detect
-    * errors (which are unfortunately quite common.)
+    * too.  This allows the simplified API to be compiled without iCCP support.
     */
    {
          static const png_byte chunks_to_process[] = {
             98,  75,  71,  68, '\0',  /* bKGD */
             99,  72,  82,  77, '\0',  /* cHRM */
+            99,  73,  67,  80, '\0',  /* cICP */
            103,  65,  77,  65, '\0',  /* gAMA */
-#        ifdef PNG_READ_iCCP_SUPPORTED
-           105,  67,  67,  80, '\0',  /* iCCP */
-#        endif
+           109,  68,  67,  86, '\0',  /* mDCV */
            115,  66,  73,  84, '\0',  /* sBIT */
            115,  82,  71,  66, '\0',  /* sRGB */
-           };
+         };
 
        /* Ignore unknown chunks and all other chunks except for the
         * IHDR, PLTE, tRNS, IDAT, and IEND chunks.
@@ -1700,7 +1521,15 @@ png_image_skip_unused_chunks(png_structrp png_ptr)
 static void
 set_file_encoding(png_image_read_control *display)
 {
-   png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma;
+   png_structrp png_ptr = display->image->opaque->png_ptr;
+   png_fixed_point g = png_resolve_file_gamma(png_ptr);
+
+   /* PNGv3: the result may be 0 however the 'default_gamma' should have been
+    * set before this is called so zero is an error:
+    */
+   if (g == 0)
+      png_error(png_ptr, "internal: default gamma not set");
+
    if (png_gamma_significant(g) != 0)
    {
       if (png_gamma_not_sRGB(g) != 0)
@@ -2188,24 +2017,18 @@ png_image_read_colormap(png_voidp argument)
    /* Default the input file gamma if required - this is necessary because
     * libpng assumes that if no gamma information is present the data is in the
     * output format, but the simplified API deduces the gamma from the input
-    * format.
+    * format.  The 'default' gamma value is also set by png_set_alpha_mode, but
+    * this is happening before any such call, so:
+    *
+    * TODO: should be an internal API and all this code should be copied into a
+    * single common gamma+colorspace file.
     */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0)
-   {
-      /* Do this directly, not using the png_colorspace functions, to ensure
-       * that it happens even if the colorspace is invalid (though probably if
-       * it is the setting will be ignored)  Note that the same thing can be
-       * achieved at the application interface with png_set_gAMA.
-       */
-      if (png_ptr->bit_depth == 16 &&
-         (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
-         png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR;
+   if (png_ptr->bit_depth == 16 &&
+      (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
+      png_ptr->default_gamma = PNG_GAMMA_LINEAR;
 
-      else
-         png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE;
-
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-   }
+   else
+      png_ptr->default_gamma = PNG_GAMMA_sRGB_INVERSE;
 
    /* Decide what to do based on the PNG color type of the input data.  The
     * utility function png_create_colormap_entry deals with most aspects of the
@@ -2583,6 +2406,8 @@ png_image_read_colormap(png_voidp argument)
 
             else
             {
+               const png_fixed_point gamma = png_resolve_file_gamma(png_ptr);
+
                /* Either the input or the output has no alpha channel, so there
                 * will be no non-opaque pixels in the color-map; it will just be
                 * grayscale.
@@ -2597,10 +2422,13 @@ png_image_read_colormap(png_voidp argument)
                 * this case and doing it in the palette; this will result in
                 * duplicate palette entries, but that's better than the
                 * alternative of double gamma correction.
+                *
+                * NOTE: PNGv3: check the resolved result of all the potentially
+                * different colour space chunks.
                 */
                if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
                   png_ptr->num_trans > 0) &&
-                  png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0)
+                  png_gamma_not_sRGB(gamma) != 0)
                {
                   cmap_entries = (unsigned int)make_gray_file_colormap(display);
                   data_encoding = P_FILE;
@@ -2632,8 +2460,8 @@ png_image_read_colormap(png_voidp argument)
                      if (output_encoding == P_sRGB)
                         gray = png_sRGB_table[gray]; /* now P_LINEAR */
 
-                     gray = PNG_DIV257(png_gamma_16bit_correct(gray,
-                         png_ptr->colorspace.gamma)); /* now P_FILE */
+                     gray = PNG_DIV257(png_gamma_16bit_correct(gray, gamma));
+                        /* now P_FILE */
 
                      /* And make sure the corresponding palette entry contains
                       * exactly the required sRGB value.
@@ -3764,6 +3592,12 @@ png_image_read_direct(png_voidp argument)
       /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise.
        */
       {
+         /* This is safe but should no longer be necessary as
+          * png_ptr->default_gamma should have been set after the
+          * info-before-IDAT was read in png_image_read_header.
+          *
+          * TODO: 1.8: remove this and see what happens.
+          */
          png_fixed_point input_gamma_default;
 
          if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 &&
@@ -3819,8 +3653,9 @@ png_image_read_direct(png_voidp argument)
           * yet; it's set below.  png_struct::gamma, however, is set to the
           * final value.
           */
-         if (png_muldiv(&gtest, output_gamma, png_ptr->colorspace.gamma,
-             PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0)
+         if (png_muldiv(&gtest, output_gamma,
+                  png_resolve_file_gamma(png_ptr), PNG_FP_1) != 0 &&
+             png_gamma_significant(gtest) == 0)
             do_local_background = 0;
 
          else if (mode == PNG_ALPHA_STANDARD)
diff --git a/source/libs/libpng/libpng-src/pngrtran.c b/source/libs/libpng/libpng-src/pngrtran.c
index 124906635..a6ce30a52 100644
--- a/source/libs/libpng/libpng-src/pngrtran.c
+++ b/source/libs/libpng/libpng-src/pngrtran.c
@@ -218,9 +218,59 @@ png_set_strip_alpha(png_structrp png_ptr)
 #endif
 
 #if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
+/* PNGv3 conformance: this private API exists to resolve the now mandatory error
+ * resolution when multiple conflicting sources of gamma or colour space
+ * information are available.
+ *
+ * Terminology (assuming power law, "gamma", encodings):
+ *    "screen" gamma: a power law imposed by the output device when digital
+ *    samples are converted to visible light output.  The EOTF - volage to
+ *    luminance on output.
+ *
+ *    "file" gamma: a power law used to encode luminance levels from the input
+ *    data (the scene or the mastering display system) into digital voltages.
+ *    The OETF - luminance to voltage on input.
+ *
+ *    gamma "correction": a power law matching the **inverse** of the overall
+ *    transfer function from input luminance levels to output levels.  The
+ *    **inverse** of the OOTF; the correction "corrects" for the OOTF by aiming
+ *    to make the overall OOTF (including the correction) linear.
+ *
+ * It is important to understand this terminology because the defined terms are
+ * scattered throughout the libpng code and it is very easy to end up with the
+ * inverse of the power law required.
+ *
+ * Variable and struct::member names:
+ *    file_gamma        OETF  how the PNG data was encoded
+ *
+ *    screen_gamma      EOTF  how the screen will decode digital levels
+ *
+ *    -- not used --    OOTF  the net effect OETF x EOTF
+ *    gamma_correction        the inverse of OOTF to make the result linear
+ *
+ * All versions of libpng require a call to "png_set_gamma" to establish the
+ * "screen" gamma, the power law representing the EOTF.  png_set_gamma may also
+ * set or default the "file" gamma; the OETF.  gamma_correction is calculated
+ * internally.
+ *
+ * The earliest libpng versions required file_gamma to be supplied to set_gamma.
+ * Later versions started allowing png_set_gamma and, later, png_set_alpha_mode,
+ * to cause defaulting from the file data.
+ *
+ * PNGv3 mandated a particular form for this defaulting, one that is compatible
+ * with what libpng did except that if libpng detected inconsistencies it marked
+ * all the chunks as "invalid".  PNGv3 effectively invalidates this prior code.
+ *
+ * Behaviour implemented below:
+ *    translate_gamma_flags(gamma, is_screen)
+ *       The libpng-1.6 API for the gamma parameters to libpng APIs
+ *       (png_set_gamma and png_set_alpha_mode at present).  This allows the
+ *       'gamma' value to be passed as a png_fixed_point number or as one of a
+ *       set of integral values for specific "well known" examples of transfer
+ *       functions.  This is compatible with PNGv3.
+ */
 static png_fixed_point
-translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,
-    int is_screen)
+translate_gamma_flags(png_fixed_point output_gamma, int is_screen)
 {
    /* Check for flag values.  The main reason for having the old Mac value as a
     * flag is that it is pretty near impossible to work out what the correct
@@ -230,14 +280,6 @@ translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,
    if (output_gamma == PNG_DEFAULT_sRGB ||
       output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB)
    {
-      /* If there is no sRGB support this just sets the gamma to the standard
-       * sRGB value.  (This is a side effect of using this function!)
-       */
-#     ifdef PNG_READ_sRGB_SUPPORTED
-         png_ptr->flags |= PNG_FLAG_ASSUME_sRGB;
-#     else
-         PNG_UNUSED(png_ptr)
-#     endif
       if (is_screen != 0)
          output_gamma = PNG_GAMMA_sRGB;
       else
@@ -279,6 +321,33 @@ convert_gamma_value(png_structrp png_ptr, double output_gamma)
    return (png_fixed_point)output_gamma;
 }
 #  endif
+
+static int
+unsupported_gamma(png_structrp png_ptr, png_fixed_point gamma, int warn)
+{
+   /* Validate a gamma value to ensure it is in a reasonable range.  The value
+    * is expected to be 1 or greater, but this range test allows for some
+    * viewing correction values.  The intent is to weed out the API users
+    * who might use the inverse of the gamma value accidentally!
+    *
+    * 1.6.47: apply the test in png_set_gamma as well but only warn and return
+    * false if it fires.
+    *
+    * TODO: 1.8: make this an app_error in png_set_gamma as well.
+    */
+   if (gamma < PNG_LIB_GAMMA_MIN || gamma > PNG_LIB_GAMMA_MAX)
+   {
+#     define msg "gamma out of supported range"
+      if (warn)
+         png_app_warning(png_ptr, msg);
+      else
+         png_app_error(png_ptr, msg);
+      return 1;
+#     undef msg
+   }
+
+   return 0;
+}
 #endif /* READ_ALPHA_MODE || READ_GAMMA */
 
 #ifdef PNG_READ_ALPHA_MODE_SUPPORTED
@@ -286,31 +355,29 @@ void PNGFAPI
 png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
     png_fixed_point output_gamma)
 {
-   int compose = 0;
    png_fixed_point file_gamma;
+   int compose = 0;
 
    png_debug(1, "in png_set_alpha_mode_fixed");
 
    if (png_rtran_ok(png_ptr, 0) == 0)
       return;
 
-   output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
-
-   /* Validate the value to ensure it is in a reasonable range.  The value
-    * is expected to be 1 or greater, but this range test allows for some
-    * viewing correction values.  The intent is to weed out the API users
-    * who might use the inverse of the gamma value accidentally!
-    *
-    * In libpng 1.6.0, we changed from 0.07..3 to 0.01..100, to accommodate
-    * the optimal 16-bit gamma of 36 and its reciprocal.
-    */
-   if (output_gamma < 1000 || output_gamma > 10000000)
-      png_error(png_ptr, "output gamma out of expected range");
+   output_gamma = translate_gamma_flags(output_gamma, 1/*screen*/);
+   if (unsupported_gamma(png_ptr, output_gamma, 0/*error*/))
+      return;
 
    /* The default file gamma is the inverse of the output gamma; the output
-    * gamma may be changed below so get the file value first:
+    * gamma may be changed below so get the file value first.  The default_gamma
+    * is set here and from the simplified API (which uses a different algorithm)
+    * so don't overwrite a set value:
     */
-   file_gamma = png_reciprocal(output_gamma);
+   file_gamma = png_ptr->default_gamma;
+   if (file_gamma == 0)
+   {
+      file_gamma = png_reciprocal(output_gamma);
+      png_ptr->default_gamma = file_gamma;
+   }
 
    /* There are really 8 possibilities here, composed of any combination
     * of:
@@ -361,17 +428,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
          png_error(png_ptr, "invalid alpha mode");
    }
 
-   /* Only set the default gamma if the file gamma has not been set (this has
-    * the side effect that the gamma in a second call to png_set_alpha_mode will
-    * be ignored.)
-    */
-   if (png_ptr->colorspace.gamma == 0)
-   {
-      png_ptr->colorspace.gamma = file_gamma;
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-   }
-
-   /* But always set the output gamma: */
+   /* Set the screen gamma values: */
    png_ptr->screen_gamma = output_gamma;
 
    /* Finally, if pre-multiplying, set the background fields to achieve the
@@ -381,7 +438,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
    {
       /* And obtain alpha pre-multiplication by composing on black: */
       memset(&png_ptr->background, 0, (sizeof png_ptr->background));
-      png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */
+      png_ptr->background_gamma = file_gamma; /* just in case */
       png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE;
       png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
 
@@ -819,8 +876,8 @@ png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
       return;
 
    /* New in libpng-1.5.4 - reserve particular negative values as flags. */
-   scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
-   file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
+   scrn_gamma = translate_gamma_flags(scrn_gamma, 1/*screen*/);
+   file_gamma = translate_gamma_flags(file_gamma, 0/*file*/);
 
    /* Checking the gamma values for being >0 was added in 1.5.4 along with the
     * premultiplied alpha support; this actually hides an undocumented feature
@@ -834,17 +891,19 @@ png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
     * libpng-1.6.0.
     */
    if (file_gamma <= 0)
-      png_error(png_ptr, "invalid file gamma in png_set_gamma");
-
+      png_app_error(png_ptr, "invalid file gamma in png_set_gamma");
    if (scrn_gamma <= 0)
-      png_error(png_ptr, "invalid screen gamma in png_set_gamma");
+      png_app_error(png_ptr, "invalid screen gamma in png_set_gamma");
 
-   /* Set the gamma values unconditionally - this overrides the value in the PNG
-    * file if a gAMA chunk was present.  png_set_alpha_mode provides a
-    * different, easier, way to default the file gamma.
+   if (unsupported_gamma(png_ptr, file_gamma, 1/*warn*/) ||
+       unsupported_gamma(png_ptr, scrn_gamma, 1/*warn*/))
+      return;
+
+   /* 1.6.47: png_struct::file_gamma and png_struct::screen_gamma are now only
+    * written by this API.  This removes dependencies on the order of API calls
+    * and allows the complex gamma checks to be delayed until needed.
     */
-   png_ptr->colorspace.gamma = file_gamma;
-   png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+   png_ptr->file_gamma = file_gamma;
    png_ptr->screen_gamma = scrn_gamma;
 }
 
@@ -1022,26 +1081,9 @@ png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
          png_ptr->rgb_to_gray_coefficients_set = 1;
       }
 
-      else
-      {
-         if (red >= 0 && green >= 0)
-            png_app_warning(png_ptr,
-                "ignoring out of range rgb_to_gray coefficients");
-
-         /* Use the defaults, from the cHRM chunk if set, else the historical
-          * values which are close to the sRGB/HDTV/ITU-Rec 709 values.  See
-          * png_do_rgb_to_gray for more discussion of the values.  In this case
-          * the coefficients are not marked as 'set' and are not overwritten if
-          * something has already provided a default.
-          */
-         if (png_ptr->rgb_to_gray_red_coeff == 0 &&
-             png_ptr->rgb_to_gray_green_coeff == 0)
-         {
-            png_ptr->rgb_to_gray_red_coeff   = 6968;
-            png_ptr->rgb_to_gray_green_coeff = 23434;
-            /* png_ptr->rgb_to_gray_blue_coeff  = 2366; */
-         }
-      }
+      else if (red >= 0 && green >= 0)
+         png_app_warning(png_ptr,
+               "ignoring out of range rgb_to_gray coefficients");
    }
 }
 
@@ -1282,6 +1324,80 @@ png_init_rgb_transformations(png_structrp png_ptr)
 #endif /* READ_EXPAND && READ_BACKGROUND */
 }
 
+#ifdef PNG_READ_GAMMA_SUPPORTED
+png_fixed_point /* PRIVATE */
+png_resolve_file_gamma(png_const_structrp png_ptr)
+{
+   png_fixed_point file_gamma;
+
+   /* The file gamma is determined by these precedence rules, in this order
+    * (i.e. use the first value found):
+    *
+    *    png_set_gamma; png_struct::file_gammma if not zero, then:
+    *    png_struct::chunk_gamma if not 0 (determined the PNGv3 rules), then:
+    *    png_set_gamma; 1/png_struct::screen_gamma if not zero
+    *
+    *    0 (i.e. do no gamma handling)
+    */
+   file_gamma = png_ptr->file_gamma;
+   if (file_gamma != 0)
+      return file_gamma;
+
+   file_gamma = png_ptr->chunk_gamma;
+   if (file_gamma != 0)
+      return file_gamma;
+
+   file_gamma = png_ptr->default_gamma;
+   if (file_gamma != 0)
+      return file_gamma;
+
+   /* If png_reciprocal oveflows it returns 0 which indicates to the caller that
+    * there is no usable file gamma.  (The checks added to png_set_gamma and
+    * png_set_alpha_mode should prevent a screen_gamma which would overflow.)
+    */
+   if (png_ptr->screen_gamma != 0)
+      file_gamma = png_reciprocal(png_ptr->screen_gamma);
+
+   return file_gamma;
+}
+
+static int
+png_init_gamma_values(png_structrp png_ptr)
+{
+   /* The following temporary indicates if overall gamma correction is
+    * required.
+    */
+   int gamma_correction = 0;
+   png_fixed_point file_gamma, screen_gamma;
+
+   /* Resolve the file_gamma.  See above: if png_ptr::screen_gamma is set
+    * file_gamma will always be set here:
+    */
+   file_gamma = png_resolve_file_gamma(png_ptr);
+   screen_gamma = png_ptr->screen_gamma;
+
+   if (file_gamma > 0) /* file has been set */
+   {
+      if (screen_gamma > 0) /* screen set too */
+         gamma_correction = png_gamma_threshold(file_gamma, screen_gamma);
+
+      else
+         /* Assume the output matches the input; a long time default behavior
+          * of libpng, although the standard has nothing to say about this.
+          */
+         screen_gamma = png_reciprocal(file_gamma);
+   }
+
+   else /* both unset, prevent corrections: */
+      file_gamma = screen_gamma = PNG_FP_1;
+
+   png_ptr->file_gamma = file_gamma;
+   png_ptr->screen_gamma = screen_gamma;
+   return gamma_correction;
+
+}
+#endif /* READ_GAMMA */
+
 void /* PRIVATE */
 png_init_read_transformations(png_structrp png_ptr)
 {
@@ -1301,59 +1417,22 @@ png_init_read_transformations(png_structrp png_ptr)
     * the test needs to be performed later - here.  In addition prior to 1.5.4
     * the tests were repeated for the PALETTE color type here - this is no
     * longer necessary (and doesn't seem to have been necessary before.)
+    *
+    * PNGv3: the new mandatory precedence/priority rules for colour space chunks
+    * are handled here (by calling the above function).
+    *
+    * Turn the gamma transformation on or off as appropriate.  Notice that
+    * PNG_GAMMA just refers to the file->screen correction.  Alpha composition
+    * may independently cause gamma correction because it needs linear data
+    * (e.g. if the file has a gAMA chunk but the screen gamma hasn't been
+    * specified.)  In any case this flag may get turned off in the code
+    * immediately below if the transform can be handled outside the row loop.
     */
-   {
-      /* The following temporary indicates if overall gamma correction is
-       * required.
-       */
-      int gamma_correction = 0;
+   if (png_init_gamma_values(png_ptr) != 0)
+      png_ptr->transformations |= PNG_GAMMA;
 
-      if (png_ptr->colorspace.gamma != 0) /* has been set */
-      {
-         if (png_ptr->screen_gamma != 0) /* screen set too */
-            gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma,
-                png_ptr->screen_gamma);
-
-         else
-            /* Assume the output matches the input; a long time default behavior
-             * of libpng, although the standard has nothing to say about this.
-             */
-            png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma);
-      }
-
-      else if (png_ptr->screen_gamma != 0)
-         /* The converse - assume the file matches the screen, note that this
-          * perhaps undesirable default can (from 1.5.4) be changed by calling
-          * png_set_alpha_mode (even if the alpha handling mode isn't required
-          * or isn't changed from the default.)
-          */
-         png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma);
-
-      else /* neither are set */
-         /* Just in case the following prevents any processing - file and screen
-          * are both assumed to be linear and there is no way to introduce a
-          * third gamma value other than png_set_background with 'UNIQUE', and,
-          * prior to 1.5.4
-          */
-         png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1;
-
-      /* We have a gamma value now. */
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-
-      /* Now turn the gamma transformation on or off as appropriate.  Notice
-       * that PNG_GAMMA just refers to the file->screen correction.  Alpha
-       * composition may independently cause gamma correction because it needs
-       * linear data (e.g. if the file has a gAMA chunk but the screen gamma
-       * hasn't been specified.)  In any case this flag may get turned off in
-       * the code immediately below if the transform can be handled outside the
-       * row loop.
-       */
-      if (gamma_correction != 0)
-         png_ptr->transformations |= PNG_GAMMA;
-
-      else
-         png_ptr->transformations &= ~PNG_GAMMA;
-   }
+   else
+      png_ptr->transformations &= ~PNG_GAMMA;
 #endif
 
    /* Certain transformations have the effect of preventing other
@@ -1425,7 +1504,7 @@ png_init_read_transformations(png_structrp png_ptr)
     * appropriately.
     */
    if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
-      png_colorspace_set_rgb_coefficients(png_ptr);
+      png_set_rgb_coefficients(png_ptr);
 #endif
 
 #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
@@ -1568,10 +1647,10 @@ png_init_read_transformations(png_structrp png_ptr)
     */
    if ((png_ptr->transformations & PNG_GAMMA) != 0 ||
        ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 &&
-        (png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
+        (png_gamma_significant(png_ptr->file_gamma) != 0 ||
          png_gamma_significant(png_ptr->screen_gamma) != 0)) ||
         ((png_ptr->transformations & PNG_COMPOSE) != 0 &&
-         (png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
+         (png_gamma_significant(png_ptr->file_gamma) != 0 ||
           png_gamma_significant(png_ptr->screen_gamma) != 0
 #  ifdef PNG_READ_BACKGROUND_SUPPORTED
          || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE &&
@@ -1627,8 +1706,8 @@ png_init_read_transformations(png_structrp png_ptr)
                      break;
 
                   case PNG_BACKGROUND_GAMMA_FILE:
-                     g = png_reciprocal(png_ptr->colorspace.gamma);
-                     gs = png_reciprocal2(png_ptr->colorspace.gamma,
+                     g = png_reciprocal(png_ptr->file_gamma);
+                     gs = png_reciprocal2(png_ptr->file_gamma,
                          png_ptr->screen_gamma);
                      break;
 
@@ -1736,8 +1815,8 @@ png_init_read_transformations(png_structrp png_ptr)
                   break;
 
                case PNG_BACKGROUND_GAMMA_FILE:
-                  g = png_reciprocal(png_ptr->colorspace.gamma);
-                  gs = png_reciprocal2(png_ptr->colorspace.gamma,
+                  g = png_reciprocal(png_ptr->file_gamma);
+                  gs = png_reciprocal2(png_ptr->file_gamma,
                       png_ptr->screen_gamma);
                   break;
 
@@ -1987,11 +2066,11 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)
     * been called before this from png_read_update_info->png_read_start_row
     * sometimes does the gamma transform and cancels the flag.
     *
-    * TODO: this looks wrong; the info_ptr should end up with a gamma equal to
-    * the screen_gamma value.  The following probably results in weirdness if
-    * the info_ptr is used by the app after the rows have been read.
+    * TODO: this is confusing.  It only changes the result of png_get_gAMA and,
+    * yes, it does return the value that the transformed data effectively has
+    * but does any app really understand this?
     */
-   info_ptr->colorspace.gamma = png_ptr->colorspace.gamma;
+   info_ptr->gamma = png_ptr->file_gamma;
 #endif
 
    if (info_ptr->bit_depth == 16)
diff --git a/source/libs/libpng/libpng-src/pngrutil.c b/source/libs/libpng/libpng-src/pngrutil.c
index a8867cb29..d0f3ed35d 100644
--- a/source/libs/libpng/libpng-src/pngrutil.c
+++ b/source/libs/libpng/libpng-src/pngrutil.c
@@ -17,6 +17,11 @@
 
 #ifdef PNG_READ_SUPPORTED
 
+/* The minimum 'zlib' stream is assumed to be just the 2 byte header, 5 bytes
+ * minimum 'deflate' stream, and the 4 byte checksum.
+ */
+#define LZ77Min  (2U+5U+4U)
+
 #ifdef PNG_READ_INTERLACING_SUPPORTED
 /* Arrays to facilitate interlacing - use pass (0 - 6) as index. */
 
@@ -43,30 +48,6 @@ png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf)
    return uval;
 }
 
-#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED)
-/* The following is a variation on the above for use with the fixed
- * point values used for gAMA and cHRM.  Instead of png_error it
- * issues a warning and returns (-1) - an invalid value because both
- * gAMA and cHRM use *unsigned* integers for fixed point values.
- */
-#define PNG_FIXED_ERROR (-1)
-
-static png_fixed_point /* PRIVATE */
-png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf)
-{
-   png_uint_32 uval = png_get_uint_32(buf);
-
-   if (uval <= PNG_UINT_31_MAX)
-      return (png_fixed_point)uval; /* known to be in range */
-
-   /* The caller can turn off the warning by passing NULL. */
-   if (png_ptr != NULL)
-      png_warning(png_ptr, "PNG fixed point integer out of range");
-
-   return PNG_FIXED_ERROR;
-}
-#endif
-
 #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
 /* NOTE: the read macros will obscure these definitions, so that if
  * PNG_USE_READ_MACROS is set the library will not use them internally,
@@ -163,6 +144,38 @@ png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
       png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
 }
 
+/* This function is called to verify that a chunk name is valid.
+ * Do this using the bit-whacking approach from contrib/tools/pngfix.c
+ *
+ * Copied from libpng 1.7.
+ */
+static int
+check_chunk_name(png_uint_32 name)
+{
+   png_uint_32 t;
+
+   /* Remove bit 5 from all but the reserved byte; this means
+    * every 8-bit unit must be in the range 65-90 to be valid.
+    * So bit 5 must be zero, bit 6 must be set and bit 7 zero.
+    */
+   name &= ~PNG_U32(32,32,0,32);
+   t = (name & ~0x1f1f1f1fU) ^ 0x40404040U;
+
+   /* Subtract 65 for each 8-bit quantity, this must not
+    * overflow and each byte must then be in the range 0-25.
+    */
+   name -= PNG_U32(65,65,65,65);
+   t |= name;
+
+   /* Subtract 26, handling the overflow which should set the
+    * top three bits of each byte.
+    */
+   name -= PNG_U32(25,25,25,26);
+   t |= ~name;
+
+   return (t & 0xe0e0e0e0U) == 0U;
+}
+
 /* Read the chunk header (length + type name).
  * Put the type name into png_ptr->chunk_name, and return the length.
  */
@@ -170,33 +183,36 @@ png_uint_32 /* PRIVATE */
 png_read_chunk_header(png_structrp png_ptr)
 {
    png_byte buf[8];
-   png_uint_32 length;
+   png_uint_32 chunk_name, length;
 
 #ifdef PNG_IO_STATE_SUPPORTED
    png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR;
 #endif
 
-   /* Read the length and the chunk name.
-    * This must be performed in a single I/O call.
+   /* Read the length and the chunk name.  png_struct::chunk_name is immediately
+    * updated even if they are detectably wrong.  This aids error message
+    * handling by allowing png_chunk_error to be used.
     */
    png_read_data(png_ptr, buf, 8);
    length = png_get_uint_31(png_ptr, buf);
+   png_ptr->chunk_name = chunk_name = PNG_CHUNK_FROM_STRING(buf+4);
 
-   /* Put the chunk name into png_ptr->chunk_name. */
-   png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4);
+   /* Reset the crc and run it over the chunk name. */
+   png_reset_crc(png_ptr);
+   png_calculate_crc(png_ptr, buf + 4, 4);
 
    png_debug2(0, "Reading chunk typeid = 0x%lx, length = %lu",
        (unsigned long)png_ptr->chunk_name, (unsigned long)length);
 
-   /* Reset the crc and run it over the chunk name. */
-   png_reset_crc(png_ptr);
-   png_calculate_crc(png_ptr, buf + 4, 4);
+   /* Sanity check the length (first by <= 0x80) and the chunk name.  An error
+    * here indicates a broken stream and libpng has no recovery from this.
+    */
+   if (buf[0] >= 0x80U)
+      png_chunk_error(png_ptr, "bad header (invalid length)");
 
    /* Check to see if chunk name is valid. */
-   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
-
-   /* Check for too-large chunk length */
-   png_check_chunk_length(png_ptr, length);
+   if (!check_chunk_name(chunk_name))
+      png_chunk_error(png_ptr, "bad header (invalid type)");
 
 #ifdef PNG_IO_STATE_SUPPORTED
    png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;
@@ -216,13 +232,85 @@ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length)
    png_calculate_crc(png_ptr, buf, length);
 }
 
+/* Compare the CRC stored in the PNG file with that calculated by libpng from
+ * the data it has read thus far.
+ */
+static int
+png_crc_error(png_structrp png_ptr, int handle_as_ancillary)
+{
+   png_byte crc_bytes[4];
+   png_uint_32 crc;
+   int need_crc = 1;
+
+   /* There are four flags two for ancillary and two for critical chunks.  The
+    * default setting of these flags is all zero.
+    *
+    * PNG_FLAG_CRC_ANCILLARY_USE
+    * PNG_FLAG_CRC_ANCILLARY_NOWARN
+    *  USE+NOWARN: no CRC calculation (implemented here), else;
+    *  NOWARN:     png_chunk_error on error (implemented in png_crc_finish)
+    *  else:       png_chunk_warning on error (implemented in png_crc_finish)
+    *              This is the default.
+    *
+    *    I.e. NOWARN without USE produces png_chunk_error.  The default setting
+    *    where neither are set does the same thing.
+    *
+    * PNG_FLAG_CRC_CRITICAL_USE
+    * PNG_FLAG_CRC_CRITICAL_IGNORE
+    *  IGNORE: no CRC calculation (implemented here), else;
+    *  USE:    png_chunk_warning on error (implemented in png_crc_finish)
+    *  else:   png_chunk_error on error (implemented in png_crc_finish)
+    *          This is the default.
+    *
+    * This arose because of original mis-implementation and has persisted for
+    * compatibility reasons.
+    *
+    * TODO: the flag names are internal so maybe this can be changed to
+    * something comprehensible.
+    */
+   if (handle_as_ancillary || PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+
+   else /* critical */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
+         need_crc = 0;
+   }
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;
+#endif
+
+   /* The chunk CRC must be serialized in a single I/O call. */
+   png_read_data(png_ptr, crc_bytes, 4);
+
+   if (need_crc != 0)
+   {
+      crc = png_get_uint_32(crc_bytes);
+      return crc != png_ptr->crc;
+   }
+
+   else
+      return 0;
+}
+
 /* Optionally skip data and then check the CRC.  Depending on whether we
  * are reading an ancillary or critical chunk, and how the program has set
  * things up, we may calculate the CRC on the data and print a message.
  * Returns '1' if there was a CRC error, '0' otherwise.
+ *
+ * There is one public version which is used in most places and another which
+ * takes the value for the 'critical' flag to check.  This allows PLTE and IEND
+ * handling code to ignore the CRC error and removes some confusing code
+ * duplication.
  */
-int /* PRIVATE */
-png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
+static int
+png_crc_finish_critical(png_structrp png_ptr, png_uint_32 skip,
+      int handle_as_ancillary)
 {
    /* The size of the local buffer for inflate is a good guess as to a
     * reasonable size to use for buffering reads from the application.
@@ -240,14 +328,24 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
       png_crc_read(png_ptr, tmpbuf, len);
    }
 
-   if (png_crc_error(png_ptr) != 0)
+   /* If 'handle_as_ancillary' has been requested and this is a critical chunk
+    * but PNG_FLAG_CRC_CRITICAL_IGNORE was set then png_read_crc did not, in
+    * fact, calculate the CRC so the ANCILLARY settings should not be used
+    * instead.
+    */
+   if (handle_as_ancillary &&
+       (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
+      handle_as_ancillary = 0;
+
+   /* TODO: this might be more comprehensible if png_crc_error was inlined here.
+    */
+   if (png_crc_error(png_ptr, handle_as_ancillary) != 0)
    {
-      if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ?
+      /* See above for the explanation of how the flags work. */
+      if (handle_as_ancillary || PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ?
           (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 :
           (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0)
-      {
          png_chunk_warning(png_ptr, "CRC error");
-      }
 
       else
          png_chunk_error(png_ptr, "CRC error");
@@ -258,61 +356,29 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
    return 0;
 }
 
-/* Compare the CRC stored in the PNG file with that calculated by libpng from
- * the data it has read thus far.
- */
 int /* PRIVATE */
-png_crc_error(png_structrp png_ptr)
+png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
 {
-   png_byte crc_bytes[4];
-   png_uint_32 crc;
-   int need_crc = 1;
-
-   if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
-   {
-      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
-          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
-         need_crc = 0;
-   }
-
-   else /* critical */
-   {
-      if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
-         need_crc = 0;
-   }
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;
-#endif
-
-   /* The chunk CRC must be serialized in a single I/O call. */
-   png_read_data(png_ptr, crc_bytes, 4);
-
-   if (need_crc != 0)
-   {
-      crc = png_get_uint_32(crc_bytes);
-      return crc != png_ptr->crc;
-   }
-
-   else
-      return 0;
+   return png_crc_finish_critical(png_ptr, skip, 0/*critical handling*/);
 }
 
 #if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\
     defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\
     defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\
-    defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_SEQUENTIAL_READ_SUPPORTED)
+    defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_eXIf_SUPPORTED) ||\
+    defined(PNG_SEQUENTIAL_READ_SUPPORTED)
 /* Manage the read buffer; this simply reallocates the buffer if it is not small
  * enough (or if it is not allocated).  The routine returns a pointer to the
  * buffer; if an error occurs and 'warn' is set the routine returns NULL, else
- * it will call png_error (via png_malloc) on failure.  (warn == 2 means
- * 'silent').
+ * it will call png_error on failure.
  */
 static png_bytep
-png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
+png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size)
 {
    png_bytep buffer = png_ptr->read_buffer;
 
+   if (new_size > png_chunk_max(png_ptr)) return NULL;
+
    if (buffer != NULL && new_size > png_ptr->read_buffer_size)
    {
       png_ptr->read_buffer = NULL;
@@ -327,24 +393,17 @@ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
 
       if (buffer != NULL)
       {
-         memset(buffer, 0, new_size); /* just in case */
+#        ifndef PNG_NO_MEMZERO /* for detecting UIM bugs **only** */
+            memset(buffer, 0, new_size); /* just in case */
+#        endif
          png_ptr->read_buffer = buffer;
          png_ptr->read_buffer_size = new_size;
       }
-
-      else if (warn < 2) /* else silent */
-      {
-         if (warn != 0)
-             png_chunk_warning(png_ptr, "insufficient memory to read chunk");
-
-         else
-             png_chunk_error(png_ptr, "insufficient memory to read chunk");
-      }
    }
 
    return buffer;
 }
-#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */
+#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|eXIf|SEQUENTIAL_READ */
 
 /* png_inflate_claim: claim the zstream for some nefarious purpose that involves
  * decompression.  Returns Z_OK on success, else a zlib error code.  It checks
@@ -631,16 +690,7 @@ png_decompress_chunk(png_structrp png_ptr,
     * maybe a '\0' terminator too.  We have to assume that 'prefix_size' is
     * limited only by the maximum chunk size.
     */
-   png_alloc_size_t limit = PNG_SIZE_MAX;
-
-# ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_malloc_max > 0 &&
-       png_ptr->user_chunk_malloc_max < limit)
-      limit = png_ptr->user_chunk_malloc_max;
-# elif PNG_USER_CHUNK_MALLOC_MAX > 0
-   if (PNG_USER_CHUNK_MALLOC_MAX < limit)
-      limit = PNG_USER_CHUNK_MALLOC_MAX;
-# endif
+   png_alloc_size_t limit = png_chunk_max(png_ptr);
 
    if (limit >= prefix_size + (terminate != 0))
    {
@@ -845,9 +895,9 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
 }
 #endif /* READ_iCCP */
 
+/* CHUNK HANDLING */
 /* Read and check the IDHR chunk */
-
-void /* PRIVATE */
+static png_handle_result_code
 png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[13];
@@ -857,12 +907,7 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_IHDR");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) != 0)
-      png_chunk_error(png_ptr, "out of place");
-
-   /* Check the length */
-   if (length != 13)
-      png_chunk_error(png_ptr, "invalid");
+   /* Length and position are checked by the caller. */
 
    png_ptr->mode |= PNG_HAVE_IHDR;
 
@@ -916,257 +961,196 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    png_debug1(3, "bit_depth = %d", png_ptr->bit_depth);
    png_debug1(3, "channels = %d", png_ptr->channels);
    png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes);
+
+   /* Rely on png_set_IHDR to completely validate the data and call png_error if
+    * it's wrong.
+    */
    png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
        color_type, interlace_type, compression_type, filter_type);
+
+   return handled_ok;
+   PNG_UNUSED(length)
 }
 
 /* Read and check the palette */
-void /* PRIVATE */
+/* TODO: there are several obvious errors in this code when handling
+ * out-of-place chunks and there is much over-complexity caused by trying to
+ * patch up the problems.
+ */
+static png_handle_result_code
 png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_color palette[PNG_MAX_PALETTE_LENGTH];
-   int max_palette_length, num, i;
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
-   png_colorp pal_ptr;
-#endif
+   png_const_charp errmsg = NULL;
 
    png_debug(1, "in png_handle_PLTE");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   /* Moved to before the 'after IDAT' check below because otherwise duplicate
-    * PLTE chunks are potentially ignored (the spec says there shall not be more
-    * than one PLTE, the error is not treated as benign, so this check trumps
-    * the requirement that PLTE appears before IDAT.)
+   /* 1.6.47: consistency.  This used to be especially treated as a critical
+    * error even in an image which is not colour mapped, there isn't a good
+    * justification for treating some errors here one way and others another so
+    * everything uses the same logic.
     */
-   else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0)
-      png_chunk_error(png_ptr, "duplicate");
+   if ((png_ptr->mode & PNG_HAVE_PLTE) != 0)
+      errmsg = "duplicate";
 
    else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      /* This is benign because the non-benign error happened before, when an
-       * IDAT was encountered in a color-mapped image with no PLTE.
-       */
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
+      errmsg = "out of place";
 
-   png_ptr->mode |= PNG_HAVE_PLTE;
+   else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
+      errmsg = "ignored in grayscale PNG";
 
-   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "ignored in grayscale PNG");
-      return;
-   }
+   else if (length > 3*PNG_MAX_PALETTE_LENGTH || (length % 3) != 0)
+      errmsg = "invalid";
 
-#ifndef PNG_READ_OPT_PLTE_SUPPORTED
-   if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-   {
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-#endif
+   /* This drops PLTE in favour of tRNS or bKGD because both of those chunks
+    * can have an effect on the rendering of the image whereas PLTE only matters
+    * in the case of an 8-bit display with a decoder which controls the palette.
+    *
+    * The alternative here is to ignore the error and store the palette anyway;
+    * destroying the tRNS will definately cause problems.
+    *
+    * NOTE: the case of PNG_COLOR_TYPE_PALETTE need not be considered because
+    * the png_handle_ routines for the three 'after PLTE' chunks tRNS, bKGD and
+    * hIST all check for a preceding PLTE in these cases.
+    */
+   else if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE &&
+            (png_has_chunk(png_ptr, tRNS) || png_has_chunk(png_ptr, bKGD)))
+      errmsg = "out of place";
 
-   if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3)
+   else
    {
-      png_crc_finish(png_ptr, length);
-
-      if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-         png_chunk_benign_error(png_ptr, "invalid");
-
-      else
-         png_chunk_error(png_ptr, "invalid");
-
-      return;
-   }
+      /* If the palette has 256 or fewer entries but is too large for the bit
+       * depth we don't issue an error to preserve the behavior of previous
+       * libpng versions. We silently truncate the unused extra palette entries
+       * here.
+       */
+      const unsigned max_palette_length =
+         (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ?
+            1U << png_ptr->bit_depth : PNG_MAX_PALETTE_LENGTH;
 
-   /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */
-   num = (int)length / 3;
+      /* The cast is safe because 'length' is less than
+       * 3*PNG_MAX_PALETTE_LENGTH
+       */
+      const unsigned num = (length > 3U*max_palette_length) ?
+         max_palette_length : (unsigned)length / 3U;
 
-   /* If the palette has 256 or fewer entries but is too large for the bit
-    * depth, we don't issue an error, to preserve the behavior of previous
-    * libpng versions. We silently truncate the unused extra palette entries
-    * here.
-    */
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      max_palette_length = (1 << png_ptr->bit_depth);
-   else
-      max_palette_length = PNG_MAX_PALETTE_LENGTH;
+      unsigned i, j;
+      png_byte buf[3*PNG_MAX_PALETTE_LENGTH];
+      png_color palette[PNG_MAX_PALETTE_LENGTH];
 
-   if (num > max_palette_length)
-      num = max_palette_length;
+      /* Read the chunk into the buffer then read to the end of the chunk. */
+      png_crc_read(png_ptr, buf, num*3U);
+      png_crc_finish_critical(png_ptr, length - 3U*num,
+            /* Handle as ancillary if PLTE is optional: */
+            png_ptr->color_type != PNG_COLOR_TYPE_PALETTE);
 
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
-   for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
-   {
-      png_byte buf[3];
+      for (i = 0U, j = 0U; i < num; i++)
+      {
+         palette[i].red = buf[j++];
+         palette[i].green = buf[j++];
+         palette[i].blue = buf[j++];
+      }
 
-      png_crc_read(png_ptr, buf, 3);
-      pal_ptr->red = buf[0];
-      pal_ptr->green = buf[1];
-      pal_ptr->blue = buf[2];
-   }
-#else
-   for (i = 0; i < num; i++)
-   {
-      png_byte buf[3];
+      /* A valid PLTE chunk has been read */
+      png_ptr->mode |= PNG_HAVE_PLTE;
 
-      png_crc_read(png_ptr, buf, 3);
-      /* Don't depend upon png_color being any order */
-      palette[i].red = buf[0];
-      palette[i].green = buf[1];
-      palette[i].blue = buf[2];
+      /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to
+       * its own copy of the palette.  This has the side effect that when
+       * png_start_row is called (this happens after any call to
+       * png_read_update_info) the info_ptr palette gets changed.  This is
+       * extremely unexpected and confusing.
+       *
+       * REVIEW: there have been consistent bugs in the past about gamma and
+       * similar transforms to colour mapped images being useless because the
+       * modified palette cannot be accessed because of the above.
+       *
+       * CONSIDER: Fix this by not sharing the palette in this way.  But does
+       * this completely fix the problem?
+       */
+      png_set_PLTE(png_ptr, info_ptr, palette, num);
+      return handled_ok;
    }
-#endif
 
-   /* If we actually need the PLTE chunk (ie for a paletted image), we do
-    * whatever the normal CRC configuration tells us.  However, if we
-    * have an RGB image, the PLTE can be considered ancillary, so
-    * we will act as though it is.
-    */
-#ifndef PNG_READ_OPT_PLTE_SUPPORTED
+   /* Here on error: errmsg is non NULL. */
    if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-#endif
    {
-      png_crc_finish(png_ptr, (png_uint_32) (length - (unsigned int)num * 3));
+      png_crc_finish(png_ptr, length);
+      png_chunk_error(png_ptr, errmsg);
    }
 
-#ifndef PNG_READ_OPT_PLTE_SUPPORTED
-   else if (png_crc_error(png_ptr) != 0)  /* Only if we have a CRC error */
+   else /* not critical to this image */
    {
-      /* If we don't want to use the data from an ancillary chunk,
-       * we have two options: an error abort, or a warning and we
-       * ignore the data in this chunk (which should be OK, since
-       * it's considered ancillary for a RGB or RGBA image).
-       *
-       * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the
-       * chunk type to determine whether to check the ancillary or the critical
-       * flags.
-       */
-      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE) == 0)
-      {
-         if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) != 0)
-            return;
-
-         else
-            png_chunk_error(png_ptr, "CRC error");
-      }
-
-      /* Otherwise, we (optionally) emit a warning and use the chunk. */
-      else if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0)
-         png_chunk_warning(png_ptr, "CRC error");
+      png_crc_finish_critical(png_ptr, length, 1/*handle as ancillary*/);
+      png_chunk_benign_error(png_ptr, errmsg);
    }
-#endif
 
-   /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its
-    * own copy of the palette.  This has the side effect that when png_start_row
-    * is called (this happens after any call to png_read_update_info) the
-    * info_ptr palette gets changed.  This is extremely unexpected and
-    * confusing.
-    *
-    * Fix this by not sharing the palette in this way.
-    */
-   png_set_PLTE(png_ptr, info_ptr, palette, num);
-
-   /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before
-    * IDAT.  Prior to 1.6.0 this was not checked; instead the code merely
-    * checked the apparent validity of a tRNS chunk inserted before PLTE on a
-    * palette PNG.  1.6.0 attempts to rigorously follow the standard and
-    * therefore does a benign error if the erroneous condition is detected *and*
-    * cancels the tRNS if the benign error returns.  The alternative is to
-    * amend the standard since it would be rather hypocritical of the standards
-    * maintainers to ignore it.
+   /* Because PNG_UNUSED(errmsg) does not work if all the uses are compiled out
+    * (this does happen).
     */
-#ifdef PNG_READ_tRNS_SUPPORTED
-   if (png_ptr->num_trans > 0 ||
-       (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0))
-   {
-      /* Cancel this because otherwise it would be used if the transforms
-       * require it.  Don't cancel the 'valid' flag because this would prevent
-       * detection of duplicate chunks.
-       */
-      png_ptr->num_trans = 0;
-
-      if (info_ptr != NULL)
-         info_ptr->num_trans = 0;
-
-      png_chunk_benign_error(png_ptr, "tRNS must be after");
-   }
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
-      png_chunk_benign_error(png_ptr, "hIST must be after");
-#endif
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
-      png_chunk_benign_error(png_ptr, "bKGD must be after");
-#endif
+   return errmsg != NULL ? handled_error : handled_error;
 }
 
-void /* PRIVATE */
+/* On read the IDAT chunk is always handled specially, even if marked for
+ * unknown handling (this is allowed), so:
+ */
+#define png_handle_IDAT NULL
+
+static png_handle_result_code
 png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_debug(1, "in png_handle_IEND");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0 ||
-       (png_ptr->mode & PNG_HAVE_IDAT) == 0)
-      png_chunk_error(png_ptr, "out of place");
-
    png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
 
-   png_crc_finish(png_ptr, length);
-
    if (length != 0)
       png_chunk_benign_error(png_ptr, "invalid");
 
+   png_crc_finish_critical(png_ptr, length, 1/*handle as ancillary*/);
+
+   return handled_ok;
    PNG_UNUSED(info_ptr)
 }
 
 #ifdef PNG_READ_gAMA_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code
 png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_fixed_point igamma;
+   png_uint_32 ugamma;
    png_byte buf[4];
 
    png_debug(1, "in png_handle_gAMA");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
+   png_crc_read(png_ptr, buf, 4);
 
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return handled_error;
 
-   if (length != 4)
+   ugamma = png_get_uint_32(buf);
+
+   if (ugamma > PNG_UINT_31_MAX)
    {
-      png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "invalid");
-      return;
+      return handled_error;
    }
 
-   png_crc_read(png_ptr, buf, 4);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+   png_set_gAMA_fixed(png_ptr, info_ptr, (png_fixed_point)/*SAFE*/ugamma);
 
-   igamma = png_get_fixed_point(NULL, buf);
+#ifdef PNG_READ_GAMMA_SUPPORTED
+      /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA.  gAMA is
+       * at the end of the chain so simply check for an unset value.
+       */
+      if (png_ptr->chunk_gamma == 0)
+         png_ptr->chunk_gamma = (png_fixed_point)/*SAFE*/ugamma;
+#endif /*READ_GAMMA*/
 
-   png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma);
-   png_colorspace_sync(png_ptr, info_ptr);
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_gAMA NULL
 #endif
 
 #ifdef PNG_READ_sBIT_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    unsigned int truelen, i;
@@ -1175,23 +1159,6 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_sBIT");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
    if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
    {
       truelen = 3;
@@ -1204,25 +1171,25 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       sample_depth = png_ptr->bit_depth;
    }
 
-   if (length != truelen || length > 4)
+   if (length != truelen)
    {
-      png_chunk_benign_error(png_ptr, "invalid");
       png_crc_finish(png_ptr, length);
-      return;
+      png_chunk_benign_error(png_ptr, "bad length");
+      return handled_error;
    }
 
    buf[0] = buf[1] = buf[2] = buf[3] = sample_depth;
    png_crc_read(png_ptr, buf, truelen);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    for (i=0; i<truelen; ++i)
    {
       if (buf[i] == 0 || buf[i] > sample_depth)
       {
          png_chunk_benign_error(png_ptr, "invalid");
-         return;
+         return handled_error;
       }
    }
 
@@ -1234,7 +1201,7 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       png_ptr->sig_bit.alpha = buf[3];
    }
 
-   else
+   else /* grayscale */
    {
       png_ptr->sig_bit.gray = buf[0];
       png_ptr->sig_bit.red = buf[0];
@@ -1244,133 +1211,132 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    }
 
    png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
+   return handled_ok;
 }
+#else
+#  define png_handle_sBIT NULL
 #endif
 
 #ifdef PNG_READ_cHRM_SUPPORTED
-void /* PRIVATE */
-png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static png_int_32
+png_get_int_32_checked(png_const_bytep buf, int *error)
 {
-   png_byte buf[32];
-   png_xy xy;
+   png_uint_32 uval = png_get_uint_32(buf);
+   if ((uval & 0x80000000) == 0) /* non-negative */
+      return (png_int_32)uval;
 
-   png_debug(1, "in png_handle_cHRM");
+   uval = (uval ^ 0xffffffff) + 1;  /* 2's complement: -x = ~x+1 */
+   if ((uval & 0x80000000) == 0) /* no overflow */
+      return -(png_int_32)uval;
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
+   /* This version of png_get_int_32 has a way of returning the error to the
+    * caller, so:
+    */
+   *error = 1;
+   return 0; /* Safe */
+}
 
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
+static png_handle_result_code /* PRIVATE */
+png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   int error = 0;
+   png_xy xy;
+   png_byte buf[32];
 
-   if (length != 32)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
+   png_debug(1, "in png_handle_cHRM");
 
    png_crc_read(png_ptr, buf, 32);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
+
+   xy.whitex = png_get_int_32_checked(buf +  0, &error);
+   xy.whitey = png_get_int_32_checked(buf +  4, &error);
+   xy.redx   = png_get_int_32_checked(buf +  8, &error);
+   xy.redy   = png_get_int_32_checked(buf + 12, &error);
+   xy.greenx = png_get_int_32_checked(buf + 16, &error);
+   xy.greeny = png_get_int_32_checked(buf + 20, &error);
+   xy.bluex  = png_get_int_32_checked(buf + 24, &error);
+   xy.bluey  = png_get_int_32_checked(buf + 28, &error);
 
-   xy.whitex = png_get_fixed_point(NULL, buf);
-   xy.whitey = png_get_fixed_point(NULL, buf + 4);
-   xy.redx   = png_get_fixed_point(NULL, buf + 8);
-   xy.redy   = png_get_fixed_point(NULL, buf + 12);
-   xy.greenx = png_get_fixed_point(NULL, buf + 16);
-   xy.greeny = png_get_fixed_point(NULL, buf + 20);
-   xy.bluex  = png_get_fixed_point(NULL, buf + 24);
-   xy.bluey  = png_get_fixed_point(NULL, buf + 28);
-
-   if (xy.whitex == PNG_FIXED_ERROR ||
-       xy.whitey == PNG_FIXED_ERROR ||
-       xy.redx   == PNG_FIXED_ERROR ||
-       xy.redy   == PNG_FIXED_ERROR ||
-       xy.greenx == PNG_FIXED_ERROR ||
-       xy.greeny == PNG_FIXED_ERROR ||
-       xy.bluex  == PNG_FIXED_ERROR ||
-       xy.bluey  == PNG_FIXED_ERROR)
+   if (error)
    {
-      png_chunk_benign_error(png_ptr, "invalid values");
-      return;
+      png_chunk_benign_error(png_ptr, "invalid");
+      return handled_error;
    }
 
-   /* If a colorspace error has already been output skip this chunk */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-      return;
+   /* png_set_cHRM may complain about some of the values but this doesn't matter
+    * because it was a cHRM and it did have vaguely (if, perhaps, ridiculous)
+    * values.  Ridiculousity will be checked if the values are used later.
+    */
+   png_set_cHRM_fixed(png_ptr, info_ptr, xy.whitex, xy.whitey, xy.redx, xy.redy,
+         xy.greenx, xy.greeny, xy.bluex, xy.bluey);
 
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0)
-   {
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
-      png_colorspace_sync(png_ptr, info_ptr);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
+   /* We only use 'chromaticities' for RGB to gray */
+#  ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+      /* There is no need to check sRGB here, cICP is NYI and iCCP is not
+       * supported so just check mDCV.
+       */
+      if (!png_has_chunk(png_ptr, mDCV))
+      {
+         png_ptr->chromaticities = xy;
+      }
+#  endif /* READ_RGB_TO_GRAY */
 
-   png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
-   (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy,
-       1/*prefer cHRM values*/);
-   png_colorspace_sync(png_ptr, info_ptr);
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_cHRM NULL
 #endif
 
 #ifdef PNG_READ_sRGB_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte intent;
 
    png_debug(1, "in png_handle_sRGB");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   if (length != 1)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_crc_read(png_ptr, &intent, 1);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   /* If a colorspace error has already been output skip this chunk */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-      return;
+      return handled_error;
 
-   /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
-    * this.
+   /* This checks the range of the "rendering intent" because it is specified in
+    * the PNG spec itself; the "reserved" values will result in the chunk not
+    * being accepted, just as they do with the various "reserved" values in
+    * IHDR.
     */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) != 0)
+   if (intent > 3/*PNGv3 spec*/)
    {
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
-      png_colorspace_sync(png_ptr, info_ptr);
-      png_chunk_benign_error(png_ptr, "too many profiles");
-      return;
+      png_chunk_benign_error(png_ptr, "invalid");
+      return handled_error;
    }
 
-   (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent);
-   png_colorspace_sync(png_ptr, info_ptr);
+   png_set_sRGB(png_ptr, info_ptr, intent);
+   /* NOTE: png_struct::chromaticities is not set here because the RGB to gray
+    * coefficients are known without a need for the chromaticities.
+    */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+      /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA.  iCCP is
+       * not supported by libpng so the only requirement is to check for cICP
+       * setting the gamma (this is NYI, but this check is safe.)
+       */
+      if (!png_has_chunk(png_ptr, cICP) || png_ptr->chunk_gamma == 0)
+         png_ptr->chunk_gamma = PNG_GAMMA_sRGB_INVERSE;
+#endif /*READ_GAMMA*/
+
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_sRGB NULL
 #endif /* READ_sRGB */
 
 #ifdef PNG_READ_iCCP_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 /* Note: this does not properly handle profiles that are > 64K under DOS */
 {
@@ -1379,44 +1345,10 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_iCCP");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   /* Consistent with all the above colorspace handling an obviously *invalid*
-    * chunk is just ignored, so does not invalidate the color space.  An
-    * alternative is to set the 'invalid' flags at the start of this routine
-    * and only clear them in they were not set before and all the tests pass.
-    */
-
-   /* The keyword must be at least one character and there is a
-    * terminator (0) byte and the compression method byte, and the
-    * 'zlib' datastream is at least 11 bytes.
-    */
-   if (length < 14)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too short");
-      return;
-   }
-
-   /* If a colorspace error has already been output skip this chunk */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
-    * this.
+   /* PNGv3: allow PNG files with both sRGB and iCCP because the PNG spec only
+    * ever said that there "should" be only one, not "shall" and the PNGv3
+    * colour chunk precedence rules give a handling for this case anyway.
     */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0)
    {
       uInt read_length, keyword_length;
       char keyword[81];
@@ -1426,19 +1358,16 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
        */
       read_length = 81; /* maximum */
       if (read_length > length)
-         read_length = (uInt)length;
+         read_length = (uInt)/*SAFE*/length;
 
       png_crc_read(png_ptr, (png_bytep)keyword, read_length);
       length -= read_length;
 
-      /* The minimum 'zlib' stream is assumed to be just the 2 byte header,
-       * 5 bytes minimum 'deflate' stream, and the 4 byte checksum.
-       */
-      if (length < 11)
+      if (length < LZ77Min)
       {
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "too short");
-         return;
+         return handled_error;
       }
 
       keyword_length = 0;
@@ -1475,15 +1404,14 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
                    */
                   png_uint_32 profile_length = png_get_uint_32(profile_header);
 
-                  if (png_icc_check_length(png_ptr, &png_ptr->colorspace,
-                      keyword, profile_length) != 0)
+                  if (png_icc_check_length(png_ptr, keyword, profile_length) !=
+                      0)
                   {
                      /* The length is apparently ok, so we can check the 132
                       * byte header.
                       */
-                     if (png_icc_check_header(png_ptr, &png_ptr->colorspace,
-                         keyword, profile_length, profile_header,
-                         png_ptr->color_type) != 0)
+                     if (png_icc_check_header(png_ptr, keyword, profile_length,
+                              profile_header, png_ptr->color_type) != 0)
                      {
                         /* Now read the tag table; a variable size buffer is
                          * needed at this point, allocate one for the whole
@@ -1493,7 +1421,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
                         png_uint_32 tag_count =
                            png_get_uint_32(profile_header + 128);
                         png_bytep profile = png_read_buffer(png_ptr,
-                            profile_length, 2/*silent*/);
+                              profile_length);
 
                         if (profile != NULL)
                         {
@@ -1512,8 +1440,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
                            if (size == 0)
                            {
                               if (png_icc_check_tag_table(png_ptr,
-                                  &png_ptr->colorspace, keyword, profile_length,
-                                  profile) != 0)
+                                       keyword, profile_length, profile) != 0)
                               {
                                  /* The profile has been validated for basic
                                   * security issues, so read the whole thing in.
@@ -1545,13 +1472,6 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
                                     png_crc_finish(png_ptr, length);
                                     finished = 1;
 
-# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
-                                    /* Check for a match against sRGB */
-                                    png_icc_set_sRGB(png_ptr,
-                                        &png_ptr->colorspace, profile,
-                                        png_ptr->zstream.adler);
-# endif
-
                                     /* Steal the profile for info_ptr. */
                                     if (info_ptr != NULL)
                                     {
@@ -1574,11 +1494,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
                                        }
 
                                        else
-                                       {
-                                          png_ptr->colorspace.flags |=
-                                             PNG_COLORSPACE_INVALID;
                                           errmsg = "out of memory";
-                                       }
                                     }
 
                                     /* else the profile remains in the read
@@ -1586,13 +1502,10 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
                                      * chunks.
                                      */
 
-                                    if (info_ptr != NULL)
-                                       png_colorspace_sync(png_ptr, info_ptr);
-
                                     if (errmsg == NULL)
                                     {
                                        png_ptr->zowner = 0;
-                                       return;
+                                       return handled_ok;
                                     }
                                  }
                                  if (errmsg == NULL)
@@ -1633,22 +1546,21 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
          errmsg = "bad keyword";
    }
 
-   else
-      errmsg = "too many profiles";
-
    /* Failure: the reason is in 'errmsg' */
    if (finished == 0)
       png_crc_finish(png_ptr, length);
 
-   png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
-   png_colorspace_sync(png_ptr, info_ptr);
    if (errmsg != NULL) /* else already output */
       png_chunk_benign_error(png_ptr, errmsg);
+
+   return handled_error;
 }
+#else
+#  define png_handle_iCCP NULL
 #endif /* READ_iCCP */
 
 #ifdef PNG_READ_sPLT_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 /* Note: this does not properly handle chunks that are > 64K under DOS */
 {
@@ -1669,43 +1581,24 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       if (png_ptr->user_chunk_cache_max == 1)
       {
          png_crc_finish(png_ptr, length);
-         return;
+         return handled_error;
       }
 
       if (--png_ptr->user_chunk_cache_max == 1)
       {
          png_warning(png_ptr, "No space in chunk cache for sPLT");
          png_crc_finish(png_ptr, length);
-         return;
+         return handled_error;
       }
    }
 #endif
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-#ifdef PNG_MAX_MALLOC_64K
-   if (length > 65535U)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too large to fit in memory");
-      return;
-   }
-#endif
-
-   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
+   buffer = png_read_buffer(png_ptr, length+1);
    if (buffer == NULL)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "out of memory");
-      return;
+      return handled_error;
    }
 
 
@@ -1716,7 +1609,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, skip) != 0)
-      return;
+      return handled_error;
 
    buffer[length] = 0;
 
@@ -1729,7 +1622,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    if (length < 2U || entry_start > buffer + (length - 2U))
    {
       png_warning(png_ptr, "malformed sPLT chunk");
-      return;
+      return handled_error;
    }
 
    new_palette.depth = *entry_start++;
@@ -1743,7 +1636,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    if ((data_length % (unsigned int)entry_size) != 0)
    {
       png_warning(png_ptr, "sPLT chunk has bad length");
-      return;
+      return handled_error;
    }
 
    dl = (png_uint_32)(data_length / (unsigned int)entry_size);
@@ -1752,7 +1645,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    if (dl > max_dl)
    {
       png_warning(png_ptr, "sPLT chunk too long");
-      return;
+      return handled_error;
    }
 
    new_palette.nentries = (png_int_32)(data_length / (unsigned int)entry_size);
@@ -1763,10 +1656,9 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    if (new_palette.entries == NULL)
    {
       png_warning(png_ptr, "sPLT chunk requires too much memory");
-      return;
+      return handled_error;
    }
 
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
    for (i = 0; i < new_palette.nentries; i++)
    {
       pp = new_palette.entries + i;
@@ -1789,31 +1681,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
       pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
    }
-#else
-   pp = new_palette.entries;
-
-   for (i = 0; i < new_palette.nentries; i++)
-   {
-
-      if (new_palette.depth == 8)
-      {
-         pp[i].red   = *entry_start++;
-         pp[i].green = *entry_start++;
-         pp[i].blue  = *entry_start++;
-         pp[i].alpha = *entry_start++;
-      }
-
-      else
-      {
-         pp[i].red   = png_get_uint_16(entry_start); entry_start += 2;
-         pp[i].green = png_get_uint_16(entry_start); entry_start += 2;
-         pp[i].blue  = png_get_uint_16(entry_start); entry_start += 2;
-         pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;
-      }
-
-      pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2;
-   }
-#endif
 
    /* Discard all chunk data except the name and stash that */
    new_palette.name = (png_charp)buffer;
@@ -1821,34 +1688,20 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
 
    png_free(png_ptr, new_palette.entries);
+   return handled_ok;
 }
+#else
+#  define png_handle_sPLT NULL
 #endif /* READ_sPLT */
 
 #ifdef PNG_READ_tRNS_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
 
    png_debug(1, "in png_handle_tRNS");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
    if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
    {
       png_byte buf[2];
@@ -1857,7 +1710,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       {
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "invalid");
-         return;
+         return handled_error;
       }
 
       png_crc_read(png_ptr, buf, 2);
@@ -1873,7 +1726,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       {
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "invalid");
-         return;
+         return handled_error;
       }
 
       png_crc_read(png_ptr, buf, length);
@@ -1887,10 +1740,9 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    {
       if ((png_ptr->mode & PNG_HAVE_PLTE) == 0)
       {
-         /* TODO: is this actually an error in the ISO spec? */
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "out of place");
-         return;
+         return handled_error;
       }
 
       if (length > (unsigned int) png_ptr->num_palette ||
@@ -1899,7 +1751,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       {
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "invalid");
-         return;
+         return handled_error;
       }
 
       png_crc_read(png_ptr, readbuf, length);
@@ -1910,13 +1762,13 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "invalid with alpha channel");
-      return;
+      return handled_error;
    }
 
    if (png_crc_finish(png_ptr, 0) != 0)
    {
       png_ptr->num_trans = 0;
-      return;
+      return handled_error;
    }
 
    /* TODO: this is a horrible side effect in the palette case because the
@@ -1925,11 +1777,14 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
     */
    png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
        &(png_ptr->trans_color));
+   return handled_ok;
 }
+#else
+#  define png_handle_tRNS NULL
 #endif
 
 #ifdef PNG_READ_bKGD_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    unsigned int truelen;
@@ -1938,27 +1793,17 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_bKGD");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
-       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-       (png_ptr->mode & PNG_HAVE_PLTE) == 0))
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
    {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
+      if ((png_ptr->mode & PNG_HAVE_PLTE) == 0)
+      {
+         png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "out of place");
+         return handled_error;
+      }
 
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
       truelen = 1;
+   }
 
    else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
       truelen = 6;
@@ -1970,13 +1815,13 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "invalid");
-      return;
+      return handled_error;
    }
 
    png_crc_read(png_ptr, buf, truelen);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    /* We convert the index value into RGB components so that we can allow
     * arbitrary RGB values for background when we have transparency, and
@@ -1992,7 +1837,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
          if (buf[0] >= info_ptr->num_palette)
          {
             png_chunk_benign_error(png_ptr, "invalid index");
-            return;
+            return handled_error;
          }
 
          background.red = (png_uint_16)png_ptr->palette[buf[0]].red;
@@ -2013,7 +1858,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
          if (buf[0] != 0 || buf[1] >= (unsigned int)(1 << png_ptr->bit_depth))
          {
             png_chunk_benign_error(png_ptr, "invalid gray level");
-            return;
+            return handled_error;
          }
       }
 
@@ -2031,7 +1876,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
          if (buf[0] != 0 || buf[2] != 0 || buf[4] != 0)
          {
             png_chunk_benign_error(png_ptr, "invalid color");
-            return;
+            return handled_error;
          }
       }
 
@@ -2043,129 +1888,87 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    }
 
    png_set_bKGD(png_ptr, info_ptr, &background);
+   return handled_ok;
 }
+#else
+#  define png_handle_bKGD NULL
 #endif
 
 #ifdef PNG_READ_cICP_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_cICP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[4];
 
    png_debug(1, "in png_handle_cICP");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cICP) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   else if (length != 4)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_crc_read(png_ptr, buf, 4);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    png_set_cICP(png_ptr, info_ptr, buf[0], buf[1],  buf[2], buf[3]);
+
+   /* We only use 'chromaticities' for RGB to gray */
+#  ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+      if (!png_has_chunk(png_ptr, mDCV))
+      {
+         /* TODO: png_ptr->chromaticities = chromaticities; */
+      }
+#  endif /* READ_RGB_TO_GRAY */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+      /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA.  cICP is
+       * at the head so simply set the gamma if it can be determined.  If not
+       * chunk_gamma remains unchanged; sRGB and gAMA handling check it for
+       * being zero.
+       */
+      /* TODO: set png_struct::chunk_gamma when possible */
+#endif /*READ_GAMMA*/
+
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_cICP NULL
 #endif
 
 #ifdef PNG_READ_cLLI_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_cLLI(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[8];
 
    png_debug(1, "in png_handle_cLLI");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cLLI) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   else if (length != 8)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_crc_read(png_ptr, buf, 8);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    /* The error checking happens here, this puts it in just one place: */
    png_set_cLLI_fixed(png_ptr, info_ptr, png_get_uint_32(buf),
          png_get_uint_32(buf+4));
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_cLLI NULL
 #endif
 
 #ifdef PNG_READ_mDCV_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_mDCV(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
+   png_xy chromaticities;
    png_byte buf[24];
 
    png_debug(1, "in png_handle_mDCV");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_mDCV) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   else if (length != 24)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_crc_read(png_ptr, buf, 24);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    /* The error checking happens here, this puts it in just one place.  The
     * odd /50000 scaling factor makes it more difficult but the (x.y) values are
@@ -2178,86 +1981,81 @@ png_handle_mDCV(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
     * stored in four bytes.  This is very, very confusing.  These APIs remove
     * the confusion by copying the existing, well established, API.
     */
+   chromaticities.redx   = png_get_uint_16(buf+ 0U) << 1; /* red x */
+   chromaticities.redy   = png_get_uint_16(buf+ 2U) << 1; /* red y */
+   chromaticities.greenx = png_get_uint_16(buf+ 4U) << 1; /* green x */
+   chromaticities.greeny = png_get_uint_16(buf+ 6U) << 1; /* green y */
+   chromaticities.bluex  = png_get_uint_16(buf+ 8U) << 1; /* blue x */
+   chromaticities.bluey  = png_get_uint_16(buf+10U) << 1; /* blue y */
+   chromaticities.whitex = png_get_uint_16(buf+12U) << 1; /* white x */
+   chromaticities.whitey = png_get_uint_16(buf+14U) << 1; /* white y */
+
    png_set_mDCV_fixed(png_ptr, info_ptr,
-         png_get_uint_16(buf+12U) << 1U, /* white x */
-         png_get_uint_16(buf+14U) << 1U, /* white y */
-         png_get_uint_16(buf+ 0U) << 1U, /* red x */
-         png_get_uint_16(buf+ 2U) << 1U, /* red y */
-         png_get_uint_16(buf+ 4U) << 1U, /* green x */
-         png_get_uint_16(buf+ 6U) << 1U, /* green y */
-         png_get_uint_16(buf+ 8U) << 1U, /* blue x */
-         png_get_uint_16(buf+10U) << 1U, /* blue y */
+         chromaticities.whitex, chromaticities.whitey,
+         chromaticities.redx, chromaticities.redy,
+         chromaticities.greenx, chromaticities.greeny,
+         chromaticities.bluex, chromaticities.bluey,
          png_get_uint_32(buf+16U), /* peak luminance */
          png_get_uint_32(buf+20U));/* minimum perceivable luminance */
+
+   /* We only use 'chromaticities' for RGB to gray */
+#  ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+      png_ptr->chromaticities = chromaticities;
+#  endif /* READ_RGB_TO_GRAY */
+
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_mDCV NULL
 #endif
 
 #ifdef PNG_READ_eXIf_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   unsigned int i;
+   png_bytep buffer = NULL;
 
    png_debug(1, "in png_handle_eXIf");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   if (length < 2)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too short");
-      return;
-   }
+   buffer = png_read_buffer(png_ptr, length);
 
-   else if (info_ptr == NULL || (info_ptr->valid & PNG_INFO_eXIf) != 0)
+   if (buffer == NULL)
    {
       png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return handled_error;
    }
 
-   info_ptr->free_me |= PNG_FREE_EXIF;
+   png_crc_read(png_ptr, buffer, length);
 
-   info_ptr->eXIf_buf = png_voidcast(png_bytep,
-             png_malloc_warn(png_ptr, length));
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return handled_error;
 
-   if (info_ptr->eXIf_buf == NULL)
+   /* PNGv3: the code used to check the byte order mark at the start for MM or
+    * II, however PNGv3 states that the the first 4 bytes should be checked.
+    * The caller ensures that there are four bytes available.
+    */
    {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
+      png_uint_32 header = png_get_uint_32(buffer);
 
-   for (i = 0; i < length; i++)
-   {
-      png_byte buf[1];
-      png_crc_read(png_ptr, buf, 1);
-      info_ptr->eXIf_buf[i] = buf[0];
-      if (i == 1)
+      /* These numbers are copied from the PNGv3 spec: */
+      if (header != 0x49492A00 && header != 0x4D4D002A)
       {
-         if ((buf[0] != 'M' && buf[0] != 'I') ||
-             (info_ptr->eXIf_buf[0] != buf[0]))
-         {
-            png_crc_finish(png_ptr, length - 2);
-            png_chunk_benign_error(png_ptr, "incorrect byte-order specifier");
-            png_free(png_ptr, info_ptr->eXIf_buf);
-            info_ptr->eXIf_buf = NULL;
-            return;
-         }
+         png_chunk_benign_error(png_ptr, "invalid");
+         return handled_error;
       }
    }
 
-   if (png_crc_finish(png_ptr, 0) == 0)
-      png_set_eXIf_1(png_ptr, info_ptr, length, info_ptr->eXIf_buf);
-
-   png_free(png_ptr, info_ptr->eXIf_buf);
-   info_ptr->eXIf_buf = NULL;
+   png_set_eXIf_1(png_ptr, info_ptr, length, buffer);
+   return handled_ok;
 }
+#else
+#  define png_handle_eXIf NULL
 #endif
 
 #ifdef PNG_READ_hIST_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    unsigned int num, i;
@@ -2265,25 +2063,13 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_hIST");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
-       (png_ptr->mode & PNG_HAVE_PLTE) == 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   num = length / 2 ;
+   /* This cast is safe because the chunk definition limits the length to a
+    * maximum of 1024 bytes.
+    *
+    * TODO: maybe use png_uint_32 anyway, not unsigned int, to reduce the
+    * casts.
+    */
+   num = (unsigned int)length / 2 ;
 
    if (length != num * 2 ||
        num != (unsigned int)png_ptr->num_palette ||
@@ -2291,7 +2077,7 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "invalid");
-      return;
+      return handled_error;
    }
 
    for (i = 0; i < num; i++)
@@ -2303,14 +2089,17 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    }
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    png_set_hIST(png_ptr, info_ptr, readbuf);
+   return handled_ok;
 }
+#else
+#  define png_handle_hIST NULL
 #endif
 
 #ifdef PNG_READ_pHYs_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[9];
@@ -2319,44 +2108,24 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_pHYs");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if (length != 9)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_crc_read(png_ptr, buf, 9);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    res_x = png_get_uint_32(buf);
    res_y = png_get_uint_32(buf + 4);
    unit_type = buf[8];
    png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_pHYs NULL
 #endif
 
 #ifdef PNG_READ_oFFs_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[9];
@@ -2365,45 +2134,25 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_oFFs");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if (length != 9)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_crc_read(png_ptr, buf, 9);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    offset_x = png_get_int_32(buf);
    offset_y = png_get_int_32(buf + 4);
    unit_type = buf[8];
    png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_oFFs NULL
 #endif
 
 #ifdef PNG_READ_pCAL_SUPPORTED
 /* Read the pCAL chunk (described in the PNG Extensions document) */
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_int_32 X0, X1;
@@ -2413,40 +2162,22 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    int i;
 
    png_debug(1, "in png_handle_pCAL");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
    png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)",
        length + 1);
 
-   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
+   buffer = png_read_buffer(png_ptr, length+1);
 
    if (buffer == NULL)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "out of memory");
-      return;
+      return handled_error;
    }
 
    png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    buffer[length] = 0; /* Null terminate the last string */
 
@@ -2462,7 +2193,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    if (endptr - buf <= 12)
    {
       png_chunk_benign_error(png_ptr, "invalid");
-      return;
+      return handled_error;
    }
 
    png_debug(3, "Reading pCAL X0, X1, type, nparams, and units");
@@ -2482,7 +2213,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
        (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
    {
       png_chunk_benign_error(png_ptr, "invalid parameter count");
-      return;
+      return handled_error;
    }
 
    else if (type >= PNG_EQUATION_LAST)
@@ -2501,7 +2232,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    if (params == NULL)
    {
       png_chunk_benign_error(png_ptr, "out of memory");
-      return;
+      return handled_error;
    }
 
    /* Get pointers to the start of each parameter string. */
@@ -2519,20 +2250,29 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       {
          png_free(png_ptr, params);
          png_chunk_benign_error(png_ptr, "invalid data");
-         return;
+         return handled_error;
       }
    }
 
    png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
        (png_charp)units, params);
 
+   /* TODO: BUG: png_set_pCAL calls png_chunk_report which, in this case, calls
+    * png_benign_error and that can error out.
+    *
+    * png_read_buffer needs to be allocated with space for both nparams and the
+    * parameter strings.  Not hard to do.
+    */
    png_free(png_ptr, params);
+   return handled_ok;
 }
+#else
+#  define png_handle_pCAL NULL
 #endif
 
 #ifdef PNG_READ_sCAL_SUPPORTED
 /* Read the sCAL chunk */
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_bytep buffer;
@@ -2540,55 +2280,29 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    int state;
 
    png_debug(1, "in png_handle_sCAL");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   /* Need unit type, width, \0, height: minimum 4 bytes */
-   else if (length < 4)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
        length + 1);
 
-   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
+   buffer = png_read_buffer(png_ptr, length+1);
 
    if (buffer == NULL)
    {
-      png_chunk_benign_error(png_ptr, "out of memory");
       png_crc_finish(png_ptr, length);
-      return;
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return handled_error;
    }
 
    png_crc_read(png_ptr, buffer, length);
    buffer[length] = 0; /* Null terminate the last string */
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    /* Validate the unit. */
    if (buffer[0] != 1 && buffer[0] != 2)
    {
       png_chunk_benign_error(png_ptr, "invalid unit");
-      return;
+      return handled_error;
    }
 
    /* Validate the ASCII numbers, need two ASCII numbers separated by
@@ -2617,15 +2331,22 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
          png_chunk_benign_error(png_ptr, "non-positive height");
 
       else
+      {
          /* This is the (only) success case. */
          png_set_sCAL_s(png_ptr, info_ptr, buffer[0],
              (png_charp)buffer+1, (png_charp)buffer+heighti);
+         return handled_ok;
+      }
    }
+
+   return handled_error;
 }
+#else
+#  define png_handle_sCAL NULL
 #endif
 
 #ifdef PNG_READ_tIME_SUPPORTED
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[7];
@@ -2633,30 +2354,17 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    png_debug(1, "in png_handle_tIME");
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
+   /* TODO: what is this doing here?  It should be happened in pngread.c and
+    * pngpread.c, although it could be moved to png_handle_chunk below and
+    * thereby avoid some code duplication.
+    */
    if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
-   if (length != 7)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
    png_crc_read(png_ptr, buf, 7);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    mod_time.second = buf[6];
    mod_time.minute = buf[5];
@@ -2666,12 +2374,16 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    mod_time.year = png_get_uint_16(buf);
 
    png_set_tIME(png_ptr, info_ptr, &mod_time);
+   return handled_ok;
+   PNG_UNUSED(length)
 }
+#else
+#  define png_handle_tIME NULL
 #endif
 
 #ifdef PNG_READ_tEXt_SUPPORTED
 /* Note: this does not properly handle chunks that are > 64K under DOS */
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_text  text_info;
@@ -2688,45 +2400,35 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       if (png_ptr->user_chunk_cache_max == 1)
       {
          png_crc_finish(png_ptr, length);
-         return;
+         return handled_error;
       }
 
       if (--png_ptr->user_chunk_cache_max == 1)
       {
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "no space in chunk cache");
-         return;
+         return handled_error;
       }
    }
 #endif
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
+   /* TODO: this doesn't work and shouldn't be necessary. */
    if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
-#ifdef PNG_MAX_MALLOC_64K
-   if (length > 65535U)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too large to fit in memory");
-      return;
-   }
-#endif
-
-   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
+   buffer = png_read_buffer(png_ptr, length+1);
 
    if (buffer == NULL)
    {
+      png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "out of memory");
-      return;
+      return handled_error;
    }
 
    png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, skip) != 0)
-      return;
+      return handled_error;
 
    key = (png_charp)buffer;
    key[length] = 0;
@@ -2745,14 +2447,19 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    text_info.text = text;
    text_info.text_length = strlen(text);
 
-   if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) != 0)
-      png_warning(png_ptr, "Insufficient memory to process text chunk");
+   if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) == 0)
+      return handled_ok;
+
+   png_chunk_benign_error(png_ptr, "out of memory");
+   return handled_error;
 }
+#else
+#  define png_handle_tEXt NULL
 #endif
 
 #ifdef PNG_READ_zTXt_SUPPORTED
 /* Note: this does not correctly handle chunks that are > 64K under DOS */
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_const_charp errmsg = NULL;
@@ -2767,40 +2474,39 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       if (png_ptr->user_chunk_cache_max == 1)
       {
          png_crc_finish(png_ptr, length);
-         return;
+         return handled_error;
       }
 
       if (--png_ptr->user_chunk_cache_max == 1)
       {
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "no space in chunk cache");
-         return;
+         return handled_error;
       }
    }
 #endif
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
+   /* TODO: should not be necessary. */
    if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
    /* Note, "length" is sufficient here; we won't be adding
-    * a null terminator later.
+    * a null terminator later.  The limit check in png_handle_chunk should be
+    * sufficient.
     */
-   buffer = png_read_buffer(png_ptr, length, 2/*silent*/);
+   buffer = png_read_buffer(png_ptr, length);
 
    if (buffer == NULL)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "out of memory");
-      return;
+      return handled_error;
    }
 
    png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    /* TODO: also check that the keyword contents match the spec! */
    for (keyword_length = 0;
@@ -2853,8 +2559,10 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
             text.lang = NULL;
             text.lang_key = NULL;
 
-            if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
-               errmsg = "insufficient memory";
+            if (png_set_text_2(png_ptr, info_ptr, &text, 1) == 0)
+               return handled_ok;
+
+            errmsg = "out of memory";
          }
       }
 
@@ -2862,14 +2570,16 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
          errmsg = png_ptr->zstream.msg;
    }
 
-   if (errmsg != NULL)
-      png_chunk_benign_error(png_ptr, errmsg);
+   png_chunk_benign_error(png_ptr, errmsg);
+   return handled_error;
 }
+#else
+#  define png_handle_zTXt NULL
 #endif
 
 #ifdef PNG_READ_iTXt_SUPPORTED
 /* Note: this does not correctly handle chunks that are > 64K under DOS */
-void /* PRIVATE */
+static png_handle_result_code /* PRIVATE */
 png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_const_charp errmsg = NULL;
@@ -2884,37 +2594,35 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       if (png_ptr->user_chunk_cache_max == 1)
       {
          png_crc_finish(png_ptr, length);
-         return;
+         return handled_error;
       }
 
       if (--png_ptr->user_chunk_cache_max == 1)
       {
          png_crc_finish(png_ptr, length);
          png_chunk_benign_error(png_ptr, "no space in chunk cache");
-         return;
+         return handled_error;
       }
    }
 #endif
 
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
+   /* TODO: should not be necessary. */
    if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
-   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
+   buffer = png_read_buffer(png_ptr, length+1);
 
    if (buffer == NULL)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "out of memory");
-      return;
+      return handled_error;
    }
 
    png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, 0) != 0)
-      return;
+      return handled_error;
 
    /* First the keyword. */
    for (prefix_length=0;
@@ -3004,8 +2712,10 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
          text.text_length = 0;
          text.itxt_length = uncompressed_length;
 
-         if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
-            errmsg = "insufficient memory";
+         if (png_set_text_2(png_ptr, info_ptr, &text, 1) == 0)
+            return handled_ok;
+
+         errmsg = "out of memory";
       }
    }
 
@@ -3014,7 +2724,10 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    if (errmsg != NULL)
       png_chunk_benign_error(png_ptr, errmsg);
+   return handled_error;
 }
+#else
+#  define png_handle_iTXt NULL
 #endif
 
 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
@@ -3022,7 +2735,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 static int
 png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length)
 {
-   png_alloc_size_t limit = PNG_SIZE_MAX;
+   const png_alloc_size_t limit = png_chunk_max(png_ptr);
 
    if (png_ptr->unknown_chunk.data != NULL)
    {
@@ -3030,16 +2743,6 @@ png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length)
       png_ptr->unknown_chunk.data = NULL;
    }
 
-#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_malloc_max > 0 &&
-       png_ptr->user_chunk_malloc_max < limit)
-      limit = png_ptr->user_chunk_malloc_max;
-
-#  elif PNG_USER_CHUNK_MALLOC_MAX > 0
-   if (PNG_USER_CHUNK_MALLOC_MAX < limit)
-      limit = PNG_USER_CHUNK_MALLOC_MAX;
-#  endif
-
    if (length <= limit)
    {
       PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name);
@@ -3078,11 +2781,11 @@ png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length)
 #endif /* READ_UNKNOWN_CHUNKS */
 
 /* Handle an unknown, or known but disabled, chunk */
-void /* PRIVATE */
+png_handle_result_code /*PRIVATE*/
 png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
     png_uint_32 length, int keep)
 {
-   int handled = 0; /* the chunk was handled */
+   png_handle_result_code handled = handled_discarded; /* the default */
 
    png_debug(1, "in png_handle_unknown");
 
@@ -3129,7 +2832,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
           *           error at this point unless it is to be saved.
           * positive: The chunk was handled, libpng will ignore/discard it.
           */
-         if (ret < 0)
+         if (ret < 0) /* handled_error */
             png_chunk_error(png_ptr, "error in user chunk");
 
          else if (ret == 0)
@@ -3163,7 +2866,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
 
          else /* chunk was handled */
          {
-            handled = 1;
+            handled = handled_ok;
             /* Critical chunks can be safely discarded at this point. */
             keep = PNG_HANDLE_CHUNK_NEVER;
          }
@@ -3248,7 +2951,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
              */
             png_set_unknown_chunks(png_ptr, info_ptr,
                 &png_ptr->unknown_chunk, 1);
-            handled = 1;
+            handled = handled_saved;
 #  ifdef PNG_USER_LIMITS_SUPPORTED
             break;
       }
@@ -3274,79 +2977,267 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
 #endif /* !READ_UNKNOWN_CHUNKS */
 
    /* Check for unhandled critical chunks */
-   if (handled == 0 && PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
+   if (handled < handled_saved && PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
       png_chunk_error(png_ptr, "unhandled critical chunk");
+
+   return handled;
 }
 
-/* This function is called to verify that a chunk name is valid.
- * This function can't have the "critical chunk check" incorporated
- * into it, since in the future we will need to be able to call user
- * functions to handle unknown critical chunks after we check that
- * the chunk name itself is valid.
+/* APNG handling: the minimal implementation of APNG handling in libpng 1.6
+ * requires that those significant applications which already handle APNG not
+ * get hosed.  To do this ensure the code here will have to ensure than APNG
+ * data by default (at least in 1.6) gets stored in the unknown chunk list.
+ * Maybe this can be relaxed in a few years but at present it's just the only
+ * safe way.
+ *
+ * ATM just cause unknown handling for all three chunks:
  */
+#define png_handle_acTL NULL
+#define png_handle_fcTL NULL
+#define png_handle_fdAT NULL
 
-/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is:
+/*
+ * 1.6.47: This is the new table driven interface to all the chunk handling.
+ *
+ * The table describes the PNG standard rules for **reading** known chunks -
+ * every chunk which has an entry in PNG_KNOWN_CHUNKS.  The table contains an
+ * entry for each PNG_INDEX_cHNK describing the rules.
  *
- * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+ * In this initial version the only information in the entry is the
+ * png_handle_cHNK function for the chunk in question.  When chunk support is
+ * compiled out the entry will be NULL.
  */
-
-void /* PRIVATE */
-png_check_chunk_name(png_const_structrp png_ptr, png_uint_32 chunk_name)
+static const struct
 {
-   int i;
-   png_uint_32 cn=chunk_name;
-
-   png_debug(1, "in png_check_chunk_name");
+   png_handle_result_code (*handler)(
+         png_structrp, png_inforp, png_uint_32 length);
+      /* A chunk-specific 'handler', NULL if the chunk is not supported in this
+       * build.
+       */
 
-   for (i=1; i<=4; ++i)
+   /* Crushing these values helps on modern 32-bit architectures because the
+    * pointer and the following bit fields both end up requiring 32 bits.
+    * Typically this will halve the table size.  On 64-bit architectures the
+    * table entries will typically be 8 bytes.
+    */
+   png_uint_32 max_length :12; /* Length min, max in bytes */
+   png_uint_32 min_length :8;
+      /* Length errors on critical chunks have special handling to preserve the
+       * existing behaviour in libpng 1.6.  Anciallary chunks are checked below
+       * and produce a 'benign' error.
+       */
+   png_uint_32 pos_before :4; /* PNG_HAVE_ values chunk must precede */
+   png_uint_32 pos_after  :4; /* PNG_HAVE_ values chunk must follow */
+      /* NOTE: PLTE, tRNS and bKGD require special handling which depends on
+       * the colour type of the base image.
+       */
+   png_uint_32 multiple   :1; /* Multiple occurences permitted */
+      /* This is enabled for PLTE because PLTE may, in practice, be optional */
+}
+read_chunks[PNG_INDEX_unknown] =
+{
+   /* Definitions as above but done indirectly by #define so that
+    * PNG_KNOWN_CHUNKS can be used safely to build the table in order.
+    *
+    * Each CDcHNK definition lists the values for the parameters **after**
+    * the first, 'handler', function.  'handler' is NULL when the chunk has no
+    * compiled in support.
+    */
+#  define NoCheck 0x801U      /* Do not check the maximum length */
+#  define Limit   0x802U      /* Limit to png_chunk_max bytes */
+#  define LKMin   3U+LZ77Min  /* Minimum length of keyword+LZ77 */
+
+#define hIHDR PNG_HAVE_IHDR
+#define hPLTE PNG_HAVE_PLTE
+#define hIDAT PNG_HAVE_IDAT
+   /* For the two chunks, tRNS and bKGD which can occur in PNGs without a PLTE
+    * but must occur after the PLTE use this and put the check in the handler
+    * routine for colour mapped images were PLTE is required.  Also put a check
+    * in PLTE for other image types to drop the PLTE if tRNS or bKGD have been
+    * seen.
+    */
+#define hCOL  (PNG_HAVE_PLTE|PNG_HAVE_IDAT)
+   /* Used for the decoding chunks which must be before PLTE. */
+#define aIDAT PNG_AFTER_IDAT
+
+   /* Chunks from W3C PNG v3: */
+   /*       cHNK  max_len,   min, before, after, multiple */
+#  define CDIHDR      13U,   13U,  hIHDR,     0,        0
+#  define CDPLTE  NoCheck,    0U,      0, hIHDR,        1
+      /* PLTE errors are only critical for colour-map images, consequently the
+       * hander does all the checks.
+       */
+#  define CDIDAT  NoCheck,    0U,  aIDAT, hIHDR,        1
+#  define CDIEND  NoCheck,    0U,      0, aIDAT,        0
+      /* Historically data was allowed in IEND */
+#  define CDtRNS     256U,    0U,  hIDAT, hIHDR,        0
+#  define CDcHRM      32U,   32U,   hCOL, hIHDR,        0
+#  define CDgAMA       4U,    4U,   hCOL, hIHDR,        0
+#  define CDiCCP  NoCheck, LKMin,   hCOL, hIHDR,        0
+#  define CDsBIT       4U,    1U,   hCOL, hIHDR,        0
+#  define CDsRGB       1U,    1U,   hCOL, hIHDR,        0
+#  define CDcICP       4U,    4U,   hCOL, hIHDR,        0
+#  define CDmDCV      24U,   24U,   hCOL, hIHDR,        0
+#  define CDeXIf    Limit,    4U,      0, hIHDR,        0
+#  define CDcLLI       8U,    8U,   hCOL, hIHDR,        0
+#  define CDtEXt  NoCheck,    2U,      0, hIHDR,        1
+      /* Allocates 'length+1'; checked in the handler */
+#  define CDzTXt    Limit, LKMin,      0, hIHDR,        1
+#  define CDiTXt  NoCheck,    6U,      0, hIHDR,        1
+      /* Allocates 'length+1'; checked in the handler */
+#  define CDbKGD       6U,    1U,  hIDAT, hIHDR,        0
+#  define CDhIST    1024U,    0U,  hPLTE, hIHDR,        0
+#  define CDpHYs       9U,    9U,  hIDAT, hIHDR,        0
+#  define CDsPLT  NoCheck,    3U,  hIDAT, hIHDR,        1
+      /* Allocates 'length+1'; checked in the handler */
+#  define CDtIME       7U,    7U,      0, hIHDR,        0
+#  define CDacTL       8U,    8U,  hIDAT, hIHDR,        0
+#  define CDfcTL      25U,   26U,      0, hIHDR,        1
+#  define CDfdAT    Limit,    4U,  hIDAT, hIHDR,        1
+   /* Supported chunks from PNG extensions 1.5.0, NYI so limit */
+#  define CDoFFs       9U,    9U,  hIDAT, hIHDR,        0
+#  define CDpCAL  NoCheck,   14U,  hIDAT, hIHDR,        0
+      /* Allocates 'length+1'; checked in the handler */
+#  define CDsCAL    Limit,    4U,  hIDAT, hIHDR,        0
+      /* Allocates 'length+1'; checked in the handler */
+
+#  define PNG_CHUNK(cHNK, index) { png_handle_ ## cHNK, CD ## cHNK },
+   PNG_KNOWN_CHUNKS
+#  undef PNG_CHUNK
+};
+
+
+static png_index
+png_chunk_index_from_name(png_uint_32 chunk_name)
+{
+   /* For chunk png_cHNK return PNG_INDEX_cHNK.  Return PNG_INDEX_unknown if
+    * chunk_name is not known.  Notice that in a particular build "known" does
+    * not necessarily mean "supported", although the inverse applies.
+    */
+   switch (chunk_name)
    {
-      int c = cn & 0xff;
+#     define PNG_CHUNK(cHNK, index)\
+         case png_ ## cHNK: return PNG_INDEX_ ## cHNK; /* == index */
 
-      if (c < 65 || c > 122 || (c > 90 && c < 97))
-         png_chunk_error(png_ptr, "invalid chunk type");
+      PNG_KNOWN_CHUNKS
 
-      cn >>= 8;
+#     undef PNG_CHUNK
+
+      default: return PNG_INDEX_unknown;
    }
 }
 
-void /* PRIVATE */
-png_check_chunk_length(png_const_structrp png_ptr, png_uint_32 length)
+png_handle_result_code /*PRIVATE*/
+png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_alloc_size_t limit = PNG_UINT_31_MAX;
-
-# ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_malloc_max > 0 &&
-       png_ptr->user_chunk_malloc_max < limit)
-      limit = png_ptr->user_chunk_malloc_max;
-# elif PNG_USER_CHUNK_MALLOC_MAX > 0
-   if (PNG_USER_CHUNK_MALLOC_MAX < limit)
-      limit = PNG_USER_CHUNK_MALLOC_MAX;
-# endif
-   if (png_ptr->chunk_name == png_IDAT)
+   /* CSE: these things don't change, these autos are just to save typing and
+    * make the code more clear.
+    */
+   const png_uint_32 chunk_name = png_ptr->chunk_name;
+   const png_index chunk_index = png_chunk_index_from_name(chunk_name);
+
+   png_handle_result_code handled = handled_error;
+   png_const_charp errmsg = NULL;
+
+   /* Is this a known chunk?  If not there are no checks performed here;
+    * png_handle_unknown does the correct checks.  This means that the values
+    * for known but unsupported chunks in the above table are not used here
+    * however the chunks_seen fields in png_struct are still set.
+    */
+   if (chunk_index == PNG_INDEX_unknown ||
+       read_chunks[chunk_index].handler == NULL)
    {
-      png_alloc_size_t idat_limit = PNG_UINT_31_MAX;
-      size_t row_factor =
-         (size_t)png_ptr->width
-         * (size_t)png_ptr->channels
-         * (png_ptr->bit_depth > 8? 2: 1)
-         + 1
-         + (png_ptr->interlaced? 6: 0);
-      if (png_ptr->height > PNG_UINT_32_MAX/row_factor)
-         idat_limit = PNG_UINT_31_MAX;
-      else
-         idat_limit = png_ptr->height * row_factor;
-      row_factor = row_factor > 32566? 32566 : row_factor;
-      idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */
-      idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX;
-      limit = limit < idat_limit? idat_limit : limit;
+      handled = png_handle_unknown(
+            png_ptr, info_ptr, length, PNG_HANDLE_CHUNK_AS_DEFAULT);
+   }
+
+   /* First check the position.   The first check is historical; the stream must
+    * start with IHDR and anything else causes libpng to give up immediately.
+    */
+   else if (chunk_index != PNG_INDEX_IHDR &&
+            (png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR"); /* NORETURN */
+
+   /* Before all the pos_before chunks, after all the pos_after chunks. */
+   else if (((png_ptr->mode & read_chunks[chunk_index].pos_before) != 0) ||
+            ((png_ptr->mode & read_chunks[chunk_index].pos_after) !=
+             read_chunks[chunk_index].pos_after))
+   {
+      errmsg = "out of place";
+   }
+
+   /* Now check for duplicates: duplicated critical chunks also produce a
+    * full error.
+    */
+   else if (read_chunks[chunk_index].multiple == 0 &&
+            png_file_has_chunk(png_ptr, chunk_index))
+   {
+      errmsg = "duplicate";
+   }
+
+   else if (length < read_chunks[chunk_index].min_length)
+      errmsg = "too short";
+   else
+   {
+      /* NOTE: apart from IHDR the critical chunks (PLTE, IDAT and IEND) are set
+       * up above not to do any length checks.
+       *
+       * The png_chunk_max check ensures that the variable length chunks are
+       * always checked at this point for being within the system allocation
+       * limits.
+       */
+      unsigned max_length = read_chunks[chunk_index].max_length;
+
+      switch (max_length)
+      {
+         case Limit:
+            /* png_read_chunk_header has already png_error'ed chunks with a
+             * length exceeding the 31-bit PNG limit, so just check the memory
+             * limit:
+             */
+            if (length <= png_chunk_max(png_ptr))
+               goto MeetsLimit;
+
+            errmsg = "length exceeds libpng limit";
+            break;
+
+         default:
+            if (length <= max_length)
+               goto MeetsLimit;
+
+            errmsg = "too long";
+            break;
+
+         case NoCheck:
+         MeetsLimit:
+            handled = read_chunks[chunk_index].handler(
+                  png_ptr, info_ptr, length);
+            break;
+      }
+   }
+
+   /* If there was an error or the chunk was simply skipped it is not counted as
+    * 'seen'.
+    */
+   if (errmsg != NULL)
+   {
+      if (PNG_CHUNK_CRITICAL(chunk_name)) /* stop immediately */
+         png_chunk_error(png_ptr, errmsg);
+      else /* ancillary chunk */
+      {
+         /* The chunk data is skipped: */
+         png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, errmsg);
+      }
    }
 
-   if (length > limit)
+   else if (handled >= handled_saved)
    {
-      png_debug2(0," length = %lu, limit = %lu",
-         (unsigned long)length,(unsigned long)limit);
-      png_benign_error(png_ptr, "chunk data is too large");
+      if (chunk_index != PNG_INDEX_unknown)
+         png_file_add_chunk(png_ptr, chunk_index);
    }
+
+   return handled;
 }
 
 /* Combines the row recently read in with the existing pixels in the row.  This
@@ -4336,6 +4227,9 @@ png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
 
          avail_in = png_ptr->IDAT_read_size;
 
+         if (avail_in > png_chunk_max(png_ptr))
+            avail_in = (uInt)/*SAFE*/png_chunk_max(png_ptr);
+
          if (avail_in > png_ptr->idat_size)
             avail_in = (uInt)png_ptr->idat_size;
 
@@ -4343,8 +4237,13 @@ png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
           * to minimize memory usage by causing lots of re-allocs, but
           * realistically doing IDAT_read_size re-allocs is not likely to be a
           * big problem.
+          *
+          * An error here corresponds to the system being out of memory.
           */
-         buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);
+         buffer = png_read_buffer(png_ptr, avail_in);
+
+         if (buffer == NULL)
+            png_chunk_error(png_ptr, "out of memory");
 
          png_crc_read(png_ptr, buffer, avail_in);
          png_ptr->idat_size -= avail_in;
diff --git a/source/libs/libpng/libpng-src/pngset.c b/source/libs/libpng/libpng-src/pngset.c
index 8e2b869d6..d7f3393c4 100644
--- a/source/libs/libpng/libpng-src/pngset.c
+++ b/source/libs/libpng/libpng-src/pngset.c
@@ -41,27 +41,21 @@ png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
     png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,
     png_fixed_point blue_x, png_fixed_point blue_y)
 {
-   png_xy xy;
-
    png_debug1(1, "in %s storage function", "cHRM fixed");
 
    if (png_ptr == NULL || info_ptr == NULL)
       return;
 
-   xy.redx = red_x;
-   xy.redy = red_y;
-   xy.greenx = green_x;
-   xy.greeny = green_y;
-   xy.bluex = blue_x;
-   xy.bluey = blue_y;
-   xy.whitex = white_x;
-   xy.whitey = white_y;
-
-   if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy,
-       2/* override with app values*/) != 0)
-      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
+   info_ptr->cHRM.redx = red_x;
+   info_ptr->cHRM.redy = red_y;
+   info_ptr->cHRM.greenx = green_x;
+   info_ptr->cHRM.greeny = green_y;
+   info_ptr->cHRM.bluex = blue_x;
+   info_ptr->cHRM.bluey = blue_y;
+   info_ptr->cHRM.whitex = white_x;
+   info_ptr->cHRM.whitey = white_y;
 
-   png_colorspace_sync_info(png_ptr, info_ptr);
+   info_ptr->valid |= PNG_INFO_cHRM;
 }
 
 void PNGFAPI
@@ -73,6 +67,7 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
     png_fixed_point int_blue_Z)
 {
    png_XYZ XYZ;
+   png_xy xy;
 
    png_debug1(1, "in %s storage function", "cHRM XYZ fixed");
 
@@ -89,11 +84,14 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
    XYZ.blue_Y = int_blue_Y;
    XYZ.blue_Z = int_blue_Z;
 
-   if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace,
-       &XYZ, 2) != 0)
-      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
+   if (png_xy_from_XYZ(&xy, &XYZ) == 0)
+   {
+      info_ptr->cHRM = xy;
+      info_ptr->valid |= PNG_INFO_cHRM;
+   }
 
-   png_colorspace_sync_info(png_ptr, info_ptr);
+   else
+      png_app_error(png_ptr, "invalid cHRM XYZ");
 }
 
 #  ifdef PNG_FLOATING_POINT_SUPPORTED
@@ -205,8 +203,7 @@ png_set_cLLI(png_const_structrp png_ptr, png_inforp info_ptr,
 
 #ifdef PNG_mDCV_SUPPORTED
 static png_uint_16
-png_ITU_fixed_16(png_const_structrp png_ptr, png_fixed_point v,
-   png_const_charp text)
+png_ITU_fixed_16(int *error, png_fixed_point v)
 {
    /* Return a safe uint16_t value scaled according to the ITU H273 rules for
     * 16-bit display chromaticities.  Functions like the corresponding
@@ -216,11 +213,10 @@ png_ITU_fixed_16(png_const_structrp png_ptr, png_fixed_point v,
     */
    v /= 2; /* rounds to 0 in C: avoids insignificant arithmetic errors */
    if (v > 65535 || v < 0)
-      png_fixed_error(png_ptr, text);
-
-#  ifndef PNG_ERROR_TEXT_SUPPORTED
-   PNG_UNUSED(text)
-#  endif
+   {
+      *error = 1;
+      return 0;
+   }
 
    return (png_uint_16)/*SAFE*/v;
 }
@@ -235,6 +231,7 @@ png_set_mDCV_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
     png_uint_32 minDL)
 {
    png_uint_16 rx, ry, gx, gy, bx, by, wx, wy;
+   int error;
 
    png_debug1(1, "in %s storage function", "mDCV");
 
@@ -242,14 +239,23 @@ png_set_mDCV_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
       return;
 
    /* Check the input values to ensure they are in the expected range: */
-   rx = png_ITU_fixed_16(png_ptr, red_x, "png_set_mDCV(red(x))");
-   ry = png_ITU_fixed_16(png_ptr, red_y, "png_set_mDCV(red(y))");
-   gx = png_ITU_fixed_16(png_ptr, green_x, "png_set_mDCV(green(x))");
-   gy = png_ITU_fixed_16(png_ptr, green_y, "png_set_mDCV(green(y))");
-   bx = png_ITU_fixed_16(png_ptr, blue_x, "png_set_mDCV(blue(x))");
-   by = png_ITU_fixed_16(png_ptr, blue_y, "png_set_mDCV(blue(y))");
-   wx = png_ITU_fixed_16(png_ptr, white_x, "png_set_mDCV(white(x))");
-   wy = png_ITU_fixed_16(png_ptr, white_y, "png_set_mDCV(white(y))");
+   error = 0;
+   rx = png_ITU_fixed_16(&error, red_x);
+   ry = png_ITU_fixed_16(&error, red_y);
+   gx = png_ITU_fixed_16(&error, green_x);
+   gy = png_ITU_fixed_16(&error, green_y);
+   bx = png_ITU_fixed_16(&error, blue_x);
+   by = png_ITU_fixed_16(&error, blue_y);
+   wx = png_ITU_fixed_16(&error, white_x);
+   wy = png_ITU_fixed_16(&error, white_y);
+
+   if (error)
+   {
+      png_chunk_report(png_ptr,
+         "mDCV chromaticities outside representable range",
+         PNG_CHUNK_WRITE_ERROR);
+      return;
+   }
 
    /* Check the light level range: */
    if (maxDL > 0x7FFFFFFFU || minDL > 0x7FFFFFFFU)
@@ -362,8 +368,8 @@ png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
    if (png_ptr == NULL || info_ptr == NULL)
       return;
 
-   png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma);
-   png_colorspace_sync_info(png_ptr, info_ptr);
+   info_ptr->gamma = file_gamma;
+   info_ptr->valid |= PNG_INFO_gAMA;
 }
 
 #  ifdef PNG_FLOATING_POINT_SUPPORTED
@@ -822,8 +828,8 @@ png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent)
    if (png_ptr == NULL || info_ptr == NULL)
       return;
 
-   (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent);
-   png_colorspace_sync_info(png_ptr, info_ptr);
+   info_ptr->rendering_intent = srgb_intent;
+   info_ptr->valid |= PNG_INFO_sRGB;
 }
 
 void PNGAPI
@@ -835,15 +841,20 @@ png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,
    if (png_ptr == NULL || info_ptr == NULL)
       return;
 
-   if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace,
-       srgb_intent) != 0)
-   {
-      /* This causes the gAMA and cHRM to be written too */
-      info_ptr->colorspace.flags |=
-         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
-   }
+   png_set_sRGB(png_ptr, info_ptr, srgb_intent);
 
-   png_colorspace_sync_info(png_ptr, info_ptr);
+#  ifdef PNG_gAMA_SUPPORTED
+      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);
+#  endif /* gAMA */
+
+#  ifdef PNG_cHRM_SUPPORTED
+      png_set_cHRM_fixed(png_ptr, info_ptr,
+         /* color      x       y */
+         /* white */ 31270, 32900,
+         /* red   */ 64000, 33000,
+         /* green */ 30000, 60000,
+         /* blue  */ 15000,  6000);
+#  endif /* cHRM */
 }
 #endif /* sRGB */
 
@@ -866,27 +877,6 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
    if (compression_type != PNG_COMPRESSION_TYPE_BASE)
       png_app_error(png_ptr, "Invalid iCCP compression method");
 
-   /* Set the colorspace first because this validates the profile; do not
-    * override previously set app cHRM or gAMA here (because likely as not the
-    * application knows better than libpng what the correct values are.)  Pass
-    * the info_ptr color_type field to png_colorspace_set_ICC because in the
-    * write case it has not yet been stored in png_ptr.
-    */
-   {
-      int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name,
-          proflen, profile, info_ptr->color_type);
-
-      png_colorspace_sync_info(png_ptr, info_ptr);
-
-      /* Don't do any of the copying if the profile was bad, or inconsistent. */
-      if (result == 0)
-         return;
-
-      /* But do write the gAMA and cHRM chunks from the profile. */
-      info_ptr->colorspace.flags |=
-         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
-   }
-
    length = strlen(name)+1;
    new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length));
 
@@ -1841,8 +1831,24 @@ png_set_chunk_malloc_max(png_structrp png_ptr,
 {
    png_debug(1, "in png_set_chunk_malloc_max");
 
+   /* pngstruct::user_chunk_malloc_max is initialized to a non-zero value in
+    * png.c.  This API supports '0' for unlimited, make sure the correct
+    * (unlimited) value is set here to avoid a need to check for 0 everywhere
+    * the parameter is used.
+    */
    if (png_ptr != NULL)
-      png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;
+   {
+      if (user_chunk_malloc_max == 0U) /* unlimited */
+      {
+#        ifdef PNG_MAX_MALLOC_64K
+            png_ptr->user_chunk_malloc_max = 65536U;
+#        else
+            png_ptr->user_chunk_malloc_max = PNG_SIZE_MAX;
+#        endif
+      }
+      else
+         png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;
+   }
 }
 #endif /* ?SET_USER_LIMITS */
 
diff --git a/source/libs/libpng/libpng-src/pngstruct.h b/source/libs/libpng/libpng-src/pngstruct.h
index 7e919d0a3..324424495 100644
--- a/source/libs/libpng/libpng-src/pngstruct.h
+++ b/source/libs/libpng/libpng-src/pngstruct.h
@@ -69,13 +69,7 @@ typedef struct png_compression_buffer
 
 /* Colorspace support; structures used in png_struct, png_info and in internal
  * functions to hold and communicate information about the color space.
- *
- * PNG_COLORSPACE_SUPPORTED is only required if the application will perform
- * colorspace corrections, otherwise all the colorspace information can be
- * skipped and the size of libpng can be reduced (significantly) by compiling
- * out the colorspace support.
  */
-#ifdef PNG_COLORSPACE_SUPPORTED
 /* The chromaticities of the red, green and blue colorants and the chromaticity
  * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)).
  */
@@ -96,48 +90,36 @@ typedef struct png_XYZ
    png_fixed_point green_X, green_Y, green_Z;
    png_fixed_point blue_X, blue_Y, blue_Z;
 } png_XYZ;
-#endif /* COLORSPACE */
 
-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
-/* A colorspace is all the above plus, potentially, profile information;
- * however at present libpng does not use the profile internally so it is only
- * stored in the png_info struct (if iCCP is supported.)  The rendering intent
- * is retained here and is checked.
- *
- * The file gamma encoding information is also stored here and gamma correction
- * is done by libpng, whereas color correction must currently be done by the
- * application.
+/* Chunk index values as an enum, PNG_INDEX_unknown is also a count of the
+ * number of chunks.
  */
-typedef struct png_colorspace
+#define PNG_CHUNK(cHNK, i) PNG_INDEX_ ## cHNK = (i),
+typedef enum
 {
-#ifdef PNG_GAMMA_SUPPORTED
-   png_fixed_point gamma;        /* File gamma */
-#endif
+   PNG_KNOWN_CHUNKS
+   PNG_INDEX_unknown
+} png_index;
+#undef PNG_CHUNK
 
-#ifdef PNG_COLORSPACE_SUPPORTED
-   png_xy      end_points_xy;    /* End points as chromaticities */
-   png_XYZ     end_points_XYZ;   /* End points as CIE XYZ colorant values */
-   png_uint_16 rendering_intent; /* Rendering intent of a profile */
-#endif
-
-   /* Flags are always defined to simplify the code. */
-   png_uint_16 flags;            /* As defined below */
-} png_colorspace, * PNG_RESTRICT png_colorspacerp;
+/* Chunk flag values.  These are (png_uint_32 values) with exactly one bit set
+ * and can be combined into a flag set with bitwise 'or'.
+ *
+ * TODO: C23: convert these macros to C23 inlines (which are static).
+ */
+#define png_chunk_flag_from_index(i) (0x80000000U >> (31 - (i)))
+   /* The flag coresponding to the given png_index enum value.  This is defined
+    * for png_unknown as well (until it reaches the value 32) but this should
+    * not be relied on.
+    */
 
-typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp;
+#define png_file_has_chunk(png_ptr, i)\
+   (((png_ptr)->chunks & png_chunk_flag_from_index(i)) != 0)
+   /* The chunk has been recorded in png_struct */
 
-/* General flags for the 'flags' field */
-#define PNG_COLORSPACE_HAVE_GAMMA           0x0001
-#define PNG_COLORSPACE_HAVE_ENDPOINTS       0x0002
-#define PNG_COLORSPACE_HAVE_INTENT          0x0004
-#define PNG_COLORSPACE_FROM_gAMA            0x0008
-#define PNG_COLORSPACE_FROM_cHRM            0x0010
-#define PNG_COLORSPACE_FROM_sRGB            0x0020
-#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040
-#define PNG_COLORSPACE_MATCHES_sRGB         0x0080 /* exact match on profile */
-#define PNG_COLORSPACE_INVALID              0x8000
-#define PNG_COLORSPACE_CANCEL(flags)        (0xffff ^ (flags))
-#endif /* COLORSPACE || GAMMA */
+#define png_file_add_chunk(pnt_ptr, i)\
+   ((void)((png_ptr)->chunks |= png_chunk_flag_from_index(i)))
+   /* Record the chunk in the png_struct */
 
 struct png_struct_def
 {
@@ -209,6 +191,11 @@ struct png_struct_def
    int zlib_set_strategy;
 #endif
 
+   png_uint_32 chunks; /* PNG_CF_ for every chunk read or (NYI) written */
+#  define png_has_chunk(png_ptr, cHNK)\
+      png_file_has_chunk(png_ptr, PNG_INDEX_ ## cHNK)
+      /* Convenience accessor - use this to check for a known chunk by name */
+
    png_uint_32 width;         /* width of image in pixels */
    png_uint_32 height;        /* height of image in pixels */
    png_uint_32 num_rows;      /* number of rows in current pass */
@@ -285,9 +272,16 @@ struct png_struct_def
    png_uint_32 flush_rows;    /* number of rows written since last flush */
 #endif
 
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+   png_xy          chromaticities; /* From mDVC, cICP, [iCCP], sRGB or cHRM */
+#endif
+
 #ifdef PNG_READ_GAMMA_SUPPORTED
    int gamma_shift;      /* number of "insignificant" bits in 16-bit gamma */
-   png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
+   png_fixed_point screen_gamma; /* screen gamma value (display exponent) */
+   png_fixed_point file_gamma;   /* file gamma value (encoding exponent) */
+   png_fixed_point chunk_gamma;  /* from cICP, iCCP, sRGB or gAMA */
+   png_fixed_point default_gamma;/* from png_set_alpha_mode */
 
    png_bytep gamma_table;     /* gamma table for 8-bit depth files */
    png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
@@ -299,7 +293,7 @@ struct png_struct_def
    png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
    png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
 #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-#endif
+#endif /* READ_GAMMA */
 
 #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
    png_color_8 sig_bit;       /* significant bits in each available channel */
@@ -349,8 +343,8 @@ struct png_struct_def
 /* To do: remove this from libpng-1.7 */
 #ifdef PNG_TIME_RFC1123_SUPPORTED
    char time_buffer[29]; /* String to hold RFC 1123 time text */
-#endif
-#endif
+#endif /* TIME_RFC1123 */
+#endif /* LIBPNG_VER < 10700 */
 
 /* New members added in libpng-1.0.6 */
 
@@ -360,8 +354,8 @@ struct png_struct_def
    png_voidp user_chunk_ptr;
 #ifdef PNG_READ_USER_CHUNKS_SUPPORTED
    png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
-#endif
-#endif
+#endif /* READ_USER_CHUNKS */
+#endif /* USER_CHUNKS */
 
 #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
    int          unknown_default; /* As PNG_HANDLE_* */
@@ -468,11 +462,5 @@ struct png_struct_def
 /* New member added in libpng-1.5.7 */
    void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info,
       png_bytep row, png_const_bytep prev_row);
-
-#ifdef PNG_READ_SUPPORTED
-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
-   png_colorspace   colorspace;
-#endif
-#endif
 };
 #endif /* PNGSTRUCT_H */
diff --git a/source/libs/libpng/libpng-src/pngtest.c b/source/libs/libpng/libpng-src/pngtest.c
index 2d54a51f3..1975b4b68 100644
--- a/source/libs/libpng/libpng-src/pngtest.c
+++ b/source/libs/libpng/libpng-src/pngtest.c
@@ -50,7 +50,7 @@
 #define STDERR stdout
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_46 Your_png_h_is_not_version_1_6_46;
+typedef png_libpng_version_1_6_47 Your_png_h_is_not_version_1_6_47;
 
 /* Ensure that all version numbers in png.h are consistent with one another. */
 #if (PNG_LIBPNG_VER != PNG_LIBPNG_VER_MAJOR * 10000 + \
@@ -2137,6 +2137,7 @@ main(int argc, char *argv[])
       fprintf(STDERR, " libpng FAILS test\n");
 
    dummy_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+#ifdef PNG_USER_LIMITS_SUPPORTED
    fprintf(STDERR, " Default limits:\n");
    fprintf(STDERR, "  width_max  = %lu\n",
        (unsigned long) png_get_user_width_max(dummy_ptr));
@@ -2152,6 +2153,7 @@ main(int argc, char *argv[])
    else
       fprintf(STDERR, "  malloc_max = %lu\n",
           (unsigned long) png_get_chunk_malloc_max(dummy_ptr));
+#endif
    png_destroy_read_struct(&dummy_ptr, NULL, NULL);
 
    return (ierror != 0);
diff --git a/source/libs/libpng/libpng-src/pngwrite.c b/source/libs/libpng/libpng-src/pngwrite.c
index f6d9d816c..b7aeff4ce 100644
--- a/source/libs/libpng/libpng-src/pngwrite.c
+++ b/source/libs/libpng/libpng-src/pngwrite.c
@@ -181,7 +181,6 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
    }
 #endif
 
-#ifdef PNG_COLORSPACE_SUPPORTED
 #  ifdef PNG_WRITE_cICP_SUPPORTED /* Priority 4 */
    if ((info_ptr->valid & PNG_INFO_cICP) != 0)
       {
@@ -193,50 +192,28 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
       }
 #  endif
 
-      /* PNG v3 change: it is now permitted to write both sRGB and ICC profiles,
-       * however because the libpng code auto-generates an sRGB for the
-       * corresponding ICC profiles and because PNG v2 disallowed this we need
-       * to only write one.
-       *
-       * Remove the PNG v2 warning about writing an sRGB ICC profile as well
-       * because it's invalid with PNG v3.
-       */
 #  ifdef PNG_WRITE_iCCP_SUPPORTED /* Priority 3 */
-         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-             (info_ptr->valid & PNG_INFO_iCCP) != 0)
+         if ((info_ptr->valid & PNG_INFO_iCCP) != 0)
          {
             png_write_iCCP(png_ptr, info_ptr->iccp_name,
-                info_ptr->iccp_profile);
+                info_ptr->iccp_profile, info_ptr->iccp_proflen);
          }
-#     ifdef PNG_WRITE_sRGB_SUPPORTED
-         else
-#     endif
 #  endif
 
 #  ifdef PNG_WRITE_sRGB_SUPPORTED /* Priority 2 */
-         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-             (info_ptr->valid & PNG_INFO_sRGB) != 0)
-            png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);
+         if ((info_ptr->valid & PNG_INFO_sRGB) != 0)
+            png_write_sRGB(png_ptr, info_ptr->rendering_intent);
 #  endif /* WRITE_sRGB */
-#endif /* COLORSPACE */
 
-#ifdef PNG_GAMMA_SUPPORTED
 #  ifdef PNG_WRITE_gAMA_SUPPORTED /* Priority 1 */
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-          (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 &&
-          (info_ptr->valid & PNG_INFO_gAMA) != 0)
-         png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);
+      if ((info_ptr->valid & PNG_INFO_gAMA) != 0)
+         png_write_gAMA_fixed(png_ptr, info_ptr->gamma);
 #  endif
-#endif
 
-#ifdef PNG_COLORSPACE_SUPPORTED
 #  ifdef PNG_WRITE_cHRM_SUPPORTED /* Also priority 1 */
-         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-             (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 &&
-             (info_ptr->valid & PNG_INFO_cHRM) != 0)
-            png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
+         if ((info_ptr->valid & PNG_INFO_cHRM) != 0)
+            png_write_cHRM_fixed(png_ptr, &info_ptr->cHRM);
 #  endif
-#endif
 
       png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
    }
diff --git a/source/libs/libpng/libpng-src/pngwutil.c b/source/libs/libpng/libpng-src/pngwutil.c
index c98475a08..be706afe6 100644
--- a/source/libs/libpng/libpng-src/pngwutil.c
+++ b/source/libs/libpng/libpng-src/pngwutil.c
@@ -1132,10 +1132,9 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent)
 /* Write an iCCP chunk */
 void /* PRIVATE */
 png_write_iCCP(png_structrp png_ptr, png_const_charp name,
-    png_const_bytep profile)
+    png_const_bytep profile, png_uint_32 profile_len)
 {
    png_uint_32 name_len;
-   png_uint_32 profile_len;
    png_byte new_name[81]; /* 1 byte for the compression byte */
    compression_state comp;
    png_uint_32 temp;
@@ -1148,11 +1147,12 @@ png_write_iCCP(png_structrp png_ptr, png_const_charp name,
    if (profile == NULL)
       png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */
 
-   profile_len = png_get_uint_32(profile);
-
    if (profile_len < 132)
       png_error(png_ptr, "ICC profile too short");
 
+   if (png_get_uint_32(profile) != profile_len)
+      png_error(png_ptr, "Incorrect data in iCCP");
+
    temp = (png_uint_32) (*(profile+8));
    if (temp > 3 && (profile_len & 0x03))
       png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
diff --git a/source/libs/libpng/libpng-src/scripts/libpng-config-head.in b/source/libs/libpng/libpng-src/scripts/libpng-config-head.in
index 230eccde1..12574fcab 100644
--- a/source/libs/libpng/libpng-src/scripts/libpng-config-head.in
+++ b/source/libs/libpng/libpng-src/scripts/libpng-config-head.in
@@ -11,7 +11,7 @@
 
 # Modeled after libxml-config.
 
-version=1.6.46
+version=1.6.47
 prefix=""
 libdir=""
 libs=""
diff --git a/source/libs/libpng/libpng-src/scripts/libpng.pc.in b/source/libs/libpng/libpng-src/scripts/libpng.pc.in
index d955d336a..10e29bfbd 100644
--- a/source/libs/libpng/libpng-src/scripts/libpng.pc.in
+++ b/source/libs/libpng/libpng-src/scripts/libpng.pc.in
@@ -5,6 +5,6 @@ includedir=@includedir@/libpng16
 
 Name: libpng
 Description: Loads and saves PNG files
-Version: 1.6.46
+Version: 1.6.47
 Libs: -L${libdir} -lpng16
 Cflags: -I${includedir}
diff --git a/source/libs/libpng/libpng-src/scripts/pnglibconf.dfa b/source/libs/libpng/libpng-src/scripts/pnglibconf.dfa
index 11b2e3a8e..f466da1a3 100644
--- a/source/libs/libpng/libpng-src/scripts/pnglibconf.dfa
+++ b/source/libs/libpng/libpng-src/scripts/pnglibconf.dfa
@@ -711,7 +711,7 @@ option WRITE_TEXT requires WRITE_ANCILLARY_CHUNKS enables TEXT
 # processing, it just validates the data in the PNG file.
 
 option GAMMA disabled
-option COLORSPACE enables GAMMA disabled
+option COLORSPACE disabled
 
 # When an ICC profile is read, or png_set, it will be checked for a match
 # against known sRGB profiles if the sRGB handling is enabled.  The
@@ -851,16 +851,16 @@ chunk cLLI
 chunk eXIf
 chunk gAMA enables GAMMA
 chunk hIST
-chunk iCCP enables COLORSPACE, GAMMA
+chunk iCCP enables GAMMA
 chunk iTXt enables TEXT
-chunk mDCV
+chunk mDCV enables COLORSPACE
 chunk oFFs
 chunk pCAL
 chunk pHYs
 chunk sBIT
 chunk sCAL
 chunk sPLT
-chunk sRGB enables COLORSPACE, GAMMA, SET_OPTION
+chunk sRGB enables GAMMA, SET_OPTION
 chunk tEXt requires TEXT
 chunk tIME
 chunk tRNS
@@ -995,7 +995,8 @@ option SIMPLIFIED_READ,
       READ_EXPAND, READ_16BIT, READ_EXPAND_16, READ_SCALE_16_TO_8,
       READ_RGB_TO_GRAY, READ_ALPHA_MODE, READ_BACKGROUND, READ_STRIP_ALPHA,
       READ_FILLER, READ_SWAP, READ_PACK, READ_GRAY_TO_RGB, READ_GAMMA,
-      READ_tRNS, READ_bKGD, READ_gAMA, READ_cHRM, READ_sRGB, READ_sBIT
+      READ_tRNS, READ_bKGD, READ_gAMA, READ_cHRM, READ_sRGB, READ_mDCV,
+      READ_cICP, READ_sBIT
 
 # AFIRST and BGR read options:
 #  Prior to libpng 1.6.8 these were disabled but switched on if the low level
diff --git a/source/libs/libpng/libpng-src/scripts/pnglibconf.h.prebuilt b/source/libs/libpng/libpng-src/scripts/pnglibconf.h.prebuilt
index b7f6928a0..748220bfc 100644
--- a/source/libs/libpng/libpng-src/scripts/pnglibconf.h.prebuilt
+++ b/source/libs/libpng/libpng-src/scripts/pnglibconf.h.prebuilt
@@ -1,6 +1,6 @@
 /* pnglibconf.h - library build configuration */
 
-/* libpng version 1.6.46 */
+/* libpng version 1.6.47 */
 
 /* Copyright (c) 2018-2025 Cosmin Truta */
 /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
diff --git a/source/libs/libpng/version.ac b/source/libs/libpng/version.ac
index 669d1f309..0e7ce2e39 100644
--- a/source/libs/libpng/version.ac
+++ b/source/libs/libpng/version.ac
@@ -8,4 +8,4 @@ dnl
 dnl --------------------------------------------------------
 dnl
 dnl  m4-include this file to define the current libpng version
-m4_define([libpng_version], [1.6.46])
+m4_define([libpng_version], [1.6.47])
diff --git a/source/tardate.ac b/source/tardate.ac
index 04046d255..c5255558a 100644
--- a/source/tardate.ac
+++ b/source/tardate.ac
@@ -1,5 +1,5 @@
-dnl $Id: tardate.ac 70573 2024-03-10 21:37:05Z karl $
-dnl   Copyright 2016-2024 Karl Berry <tex-live@tug.org>
+dnl $Id: tardate.ac 74485 2025-03-06 22:52:00Z karl $
+dnl   Copyright 2016-2025 Karl Berry <tex-live@tug.org>
 dnl   Copyright 2010-2015 Peter Breitenlohner <tex-live@tug.org>
 dnl
 dnl   This file is free software; the copyright holder
@@ -9,4 +9,4 @@ dnl
 dnl --------------------------------------------------------
 dnl
 dnl  m4-include this file to define the current TeX Live tarball version
-m4_define([tex_live_tardate], [2024-03-10])
+m4_define([tex_live_tardate], [2025-03-07])
diff --git a/source/texk/README b/source/texk/README
index 087899d4e..632d7b449 100644
--- a/source/texk/README
+++ b/source/texk/README
@@ -1,4 +1,4 @@
-$Id: README 74112 2025-02-18 21:53:39Z karl $
+$Id: README 74361 2025-02-28 23:31:39Z karl $
 Copyright 2006-2025 TeX Users Group.
 You may freely use, modify and/or distribute this file.
 
@@ -67,7 +67,7 @@ dvisvgm 3.4.3 - checked 11jan25
   https://github.com/mgieseki/dvisvgm
   https://ctan.org/pkg/dvisvgm
 
-gregorio 6.1.0beta2 - checked 18feb25
+gregorio 6.1.0 - checked 28feb25
   https://mirror.ctan.org/support/gregoriotex/
 
 gsftopk - from Paul Vojta's xdvi.
diff --git a/source/texk/configure b/source/texk/configure
index a2148cf9e..d27dbc134 100755
--- a/source/texk/configure
+++ b/source/texk/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for TeX Live texk 2025.
+# Generated by GNU Autoconf 2.72 for TeX Live texk 2026/dev.
 #
 # Report bugs to <tex-live@tug.org>.
 #
@@ -603,8 +603,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='TeX Live texk'
 PACKAGE_TARNAME='tex-live-texk'
-PACKAGE_VERSION='2025'
-PACKAGE_STRING='TeX Live texk 2025'
+PACKAGE_VERSION='2026/dev'
+PACKAGE_STRING='TeX Live texk 2026/dev'
 PACKAGE_BUGREPORT='tex-live@tug.org'
 PACKAGE_URL=''
 
@@ -1382,7 +1382,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 TeX Live texk 2025 to adapt to many kinds of systems.
+'configure' configures TeX Live texk 2026/dev to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1453,7 +1453,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of TeX Live texk 2025:";;
+     short | recursive ) echo "Configuration of TeX Live texk 2026/dev:";;
    esac
   cat <<\_ACEOF
 
@@ -1639,7 +1639,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-TeX Live texk configure 2025
+TeX Live texk configure 2026/dev
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -1799,7 +1799,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 TeX Live texk $as_me 2025, which was
+It was created by TeX Live texk $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -5776,7 +5776,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='tex-live-texk'
- VERSION='2025'
+ VERSION='2026/dev'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -6701,7 +6701,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 TeX Live texk $as_me 2025, which was
+This file was extended by TeX Live texk $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6760,7 +6760,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="\\
-TeX Live texk config.status 2025
+TeX Live texk config.status 2026/dev
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/texk/kpathsea/ChangeLog b/source/texk/kpathsea/ChangeLog
index 995d64077..9a80b60c6 100644
--- a/source/texk/kpathsea/ChangeLog
+++ b/source/texk/kpathsea/ChangeLog
@@ -1,3 +1,11 @@
+2025-03-08  Karl Berry  <karl@tug.org>
+
+	* version.ac: 6.4.2/dev.
+
+2025-03-07  Karl Berry  <karl@tug.org>
+
+	* TL'25 release.
+
 2025-02-12  Karl Berry  <karl@freefriends.org>
 
 	* texmf.cnf (shell_escape_commands): doc link to
diff --git a/source/texk/kpathsea/NEWS b/source/texk/kpathsea/NEWS
index 7f14c89ea..147b4aa64 100644
--- a/source/texk/kpathsea/NEWS
+++ b/source/texk/kpathsea/NEWS
@@ -1,7 +1,7 @@
-$Id: NEWS 73591 2025-01-25 18:41:15Z karl $
+$Id: NEWS 74485 2025-03-06 22:52:00Z karl $
 This file records noteworthy changes.  (Public domain.)
 
-6.4.1 (for TeX Live 2025)
+6.4.1 (for TeX Live 2025, 7 March 2025)
 * kpsewhich outputs a blank line when a given file cannot be found,
   if more than one file to search for is specified.  
 
diff --git a/source/texk/kpathsea/c-auto.in b/source/texk/kpathsea/c-auto.in
index 8a22aa67c..8ffe4c8a6 100644
--- a/source/texk/kpathsea/c-auto.in
+++ b/source/texk/kpathsea/c-auto.in
@@ -23,7 +23,7 @@
 #define KPATHSEA_C_AUTO_H
 
 /* kpathsea: the version string. */
-#define KPSEVERSION "kpathsea version 6.4.1"
+#define KPSEVERSION "kpathsea version 6.4.2/dev"
 
 /* Define to 1 if the 'closedir' function returns void instead of int. */
 #undef CLOSEDIR_VOID
diff --git a/source/texk/kpathsea/configure b/source/texk/kpathsea/configure
index 851b45c04..1ab8dc140 100755
--- a/source/texk/kpathsea/configure
+++ b/source/texk/kpathsea/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for Kpathsea 6.4.1.
+# Generated by GNU Autoconf 2.72 for Kpathsea 6.4.2/dev.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -614,8 +614,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='Kpathsea'
 PACKAGE_TARNAME='kpathsea'
-PACKAGE_VERSION='6.4.1'
-PACKAGE_STRING='Kpathsea 6.4.1'
+PACKAGE_VERSION='6.4.2/dev'
+PACKAGE_STRING='Kpathsea 6.4.2/dev'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1376,7 +1376,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 Kpathsea 6.4.1 to adapt to many kinds of systems.
+'configure' configures Kpathsea 6.4.2/dev to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1447,7 +1447,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Kpathsea 6.4.1:";;
+     short | recursive ) echo "Configuration of Kpathsea 6.4.2/dev:";;
    esac
   cat <<\_ACEOF
 
@@ -1576,7 +1576,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Kpathsea configure 6.4.1
+Kpathsea configure 6.4.2/dev
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -2357,7 +2357,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 Kpathsea $as_me 6.4.1, which was
+It was created by Kpathsea $as_me 6.4.2/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3137,14 +3137,14 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
-KPSEVERSION=6.4.1
+KPSEVERSION=6.4.2/dev
 
 
-KPSE_LT_VERSINFO=10:1:4
+KPSE_LT_VERSINFO=10:2:4
 
 
 
- WEB2CVERSION=" (TeX Live 2025)"
+ WEB2CVERSION=" (TeX Live 2026/dev)"
 
 
 am__api_version='1.17'
@@ -9259,7 +9259,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='kpathsea'
- VERSION='6.4.1'
+ VERSION='6.4.2/dev'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -16888,7 +16888,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 Kpathsea $as_me 6.4.1, which was
+This file was extended by Kpathsea $as_me 6.4.2/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -16956,7 +16956,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="\\
-Kpathsea config.status 6.4.1
+Kpathsea config.status 6.4.2/dev
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/texk/kpathsea/version.ac b/source/texk/kpathsea/version.ac
index 52959db0a..8b7d26c1e 100644
--- a/source/texk/kpathsea/version.ac
+++ b/source/texk/kpathsea/version.ac
@@ -1,4 +1,4 @@
-dnl $Id: version.ac 73591 2025-01-25 18:41:15Z karl $
+dnl $Id: version.ac 74529 2025-03-08 18:17:45Z karl $
 dnl   Copyright 2016-2025 Karl Berry <tex-live@tug.org>
 dnl   Copyright 2011-2015 Peter Breitenlohner <tex-live@tug.org>
 dnl
@@ -23,4 +23,4 @@ dnl   releases.
 dnl --------------------------------------------------------
 dnl
 dnl This file is m4-included from configure.ac.
-m4_define([kpse_version], [6.4.1])
+m4_define([kpse_version], [6.4.2/dev])
diff --git a/source/texk/texlive/configure b/source/texk/texlive/configure
index e218d2480..6657a12ad 100755
--- a/source/texk/texlive/configure
+++ b/source/texk/texlive/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for TeX Live Scripts 2025.
+# Generated by GNU Autoconf 2.72 for TeX Live Scripts 2026/dev.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -603,8 +603,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='TeX Live Scripts'
 PACKAGE_TARNAME='tex-live-scripts'
-PACKAGE_VERSION='2025'
-PACKAGE_STRING='TeX Live Scripts 2025'
+PACKAGE_VERSION='2026/dev'
+PACKAGE_STRING='TeX Live Scripts 2026/dev'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1276,7 +1276,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 TeX Live Scripts 2025 to adapt to many kinds of systems.
+'configure' configures TeX Live Scripts 2026/dev to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1344,7 +1344,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of TeX Live Scripts 2025:";;
+     short | recursive ) echo "Configuration of TeX Live Scripts 2026/dev:";;
    esac
   cat <<\_ACEOF
 
@@ -1440,7 +1440,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-TeX Live Scripts configure 2025
+TeX Live Scripts configure 2026/dev
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -1517,7 +1517,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 TeX Live Scripts $as_me 2025, which was
+It was created by TeX Live Scripts $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -2986,7 +2986,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='tex-live-scripts'
- VERSION='2025'
+ VERSION='2026/dev'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -5129,7 +5129,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 TeX Live Scripts $as_me 2025, which was
+This file was extended by TeX Live Scripts $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -5188,7 +5188,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="\\
-TeX Live Scripts config.status 2025
+TeX Live Scripts config.status 2026/dev
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/texk/web2c/ChangeLog b/source/texk/web2c/ChangeLog
index fa66ecfca..992baa508 100644
--- a/source/texk/web2c/ChangeLog
+++ b/source/texk/web2c/ChangeLog
@@ -1,3 +1,17 @@
+2025-03-10  Andreas Scherer  <https://ascherer.github.io>
+
+	* texk/web2c/tangle.ch,
+	* texk/web2c/tangleboot.pin: Reject strings as macro names.
+	Report and initial patch from Benjamin Gray
+	(https://tug.org/pipermail/tex-k/2025-March/004163.html).
+
+2025-03-10  Andreas Scherer  <https://ascherer.github.io>
+
+	* texk/web2c/tangle.ch,
+	* texk/web2c/tangleboot.pin: Change case for letters only.
+	Report and initial patch from Benjamin Gray
+	(https://tug.org/pipermail/tex-k/2025-March/004161.html).
+
 2025-02-01  Karl Berry  <karl@freefriends.org>
 
 	* tex.ch (24.336): comment out this patch for interactive deletion
diff --git a/source/texk/web2c/NEWS b/source/texk/web2c/NEWS
index c2ebd02a4..34060576d 100644
--- a/source/texk/web2c/NEWS
+++ b/source/texk/web2c/NEWS
@@ -2,7 +2,7 @@ This file records noteworthy changes.  (Public domain.)
 See also */NEWS, */ChangeLog, etc.
 
 
-2025 (for TeX Live 2025)
+2025 (for TeX Live 2025, 7 March 2025)
 * Most engines: new primitive parameter \ignoreprimitiveerror: if set to 1,
   the error "Infinite glue shrinkage found in box being split"
   becomes a warning (thus program exit status remains 0). Other values
diff --git a/source/texk/web2c/configure b/source/texk/web2c/configure
index 08a7cac77..548d8dc25 100755
--- a/source/texk/web2c/configure
+++ b/source/texk/web2c/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for Web2C 2025.
+# Generated by GNU Autoconf 2.72 for Web2C 2026/dev.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -614,8 +614,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='Web2C'
 PACKAGE_TARNAME='web2c'
-PACKAGE_VERSION='2025'
-PACKAGE_STRING='Web2C 2025'
+PACKAGE_VERSION='2026/dev'
+PACKAGE_STRING='Web2C 2026/dev'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1672,7 +1672,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 Web2C 2025 to adapt to many kinds of systems.
+'configure' configures Web2C 2026/dev to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1747,7 +1747,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Web2C 2025:";;
+     short | recursive ) echo "Configuration of Web2C 2026/dev:";;
    esac
   cat <<\_ACEOF
 
@@ -1979,7 +1979,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Web2C configure 2025
+Web2C configure 2026/dev
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -3001,7 +3001,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 Web2C $as_me 2025, which was
+It was created by Web2C $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3997,7 +3997,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
-WEB2CVERSION=2025
+WEB2CVERSION=2026/dev
 
 
 # LuaTeX requires system extensions for socket support.
@@ -9993,7 +9993,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='web2c'
- VERSION='2025'
+ VERSION='2026/dev'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -27427,7 +27427,7 @@ Usage: $0 [OPTIONS]
 Report bugs to <bug-libtool@gnu.org>."
 
 lt_cl_version="\
-Web2C config.lt 2025
+Web2C config.lt 2026/dev
 configured by $0, generated by GNU Autoconf 2.72.
 
 Copyright (C) 2024 Free Software Foundation, Inc.
@@ -31345,7 +31345,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 Web2C $as_me 2025, which was
+This file was extended by Web2C $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -31417,7 +31417,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="\\
-Web2C config.status 2025
+Web2C config.status 2026/dev
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/texk/web2c/cwebdir/ChangeLog b/source/texk/web2c/cwebdir/ChangeLog
index bd7bbe0d7..1ed6c99a6 100644
--- a/source/texk/web2c/cwebdir/ChangeLog
+++ b/source/texk/web2c/cwebdir/ChangeLog
@@ -1,3 +1,11 @@
+2025-02-26  Andreas Scherer  <https://ascherer.github.io>
+
+	* tests/ham-sorted.tex,
+	* tests/ham.ch,
+	* tests/ham.ref,
+	* tests/ham.sref,
+	* tests/ham.tex: Command-line option 'm' is no SGB variable.
+
 2025-02-02  Andreas Scherer  <https://ascherer.github.io>
 
 	* ctwill-mini.ch: Avoid several 'Overfull \vbox'es.
diff --git a/source/texk/web2c/lib/ChangeLog b/source/texk/web2c/lib/ChangeLog
index b3e8f9902..2ea05b671 100644
--- a/source/texk/web2c/lib/ChangeLog
+++ b/source/texk/web2c/lib/ChangeLog
@@ -1,3 +1,12 @@
+2025-03-07  Karl Berry  <karl@tug.org>
+
+	* TL'25 release.
+
+2025-03-07  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	* texmfmp.c (gettexstring) [XETEX]: correct non-BMP characters in
+	filenames with synctex.
+
 2025-02-28  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
 
 	* texmfmp.c: Remove problematic lines for windows (windows only)
diff --git a/source/texk/web2c/lib/texmfmp.c b/source/texk/web2c/lib/texmfmp.c
index ad208e98c..d7d4d2547 100644
--- a/source/texk/web2c/lib/texmfmp.c
+++ b/source/texk/web2c/lib/texmfmp.c
@@ -3192,7 +3192,7 @@ gettexstring (strnumber s)
     if (c >= 0xD800 && c <= 0xDBFF) {
       unsigned lo = strpool[++i + strstart[s - 65536L]];
       if (lo >= 0xDC00 && lo <= 0xDFFF)
-        c = (c - 0xD800) * 0x0400 + lo - 0xDC00;
+        c = 0x10000 + (c - 0xD800) * 0x0400 + lo - 0xDC00;
       else
         c = 0xFFFD;
     }
diff --git a/source/texk/web2c/luatexdir/luatex_svnversion.h b/source/texk/web2c/luatexdir/luatex_svnversion.h
index e335dc7a2..935899d50 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 7669
+#define luatex_svn_revision 7670
 #endif
diff --git a/source/texk/web2c/man/ChangeLog b/source/texk/web2c/man/ChangeLog
index 5c4208bb6..e999a9d4e 100644
--- a/source/texk/web2c/man/ChangeLog
+++ b/source/texk/web2c/man/ChangeLog
@@ -1,3 +1,7 @@
+2025-03-07  Karl Berry  <karl@tug.org>
+
+	* TL'25 release.
+
 2025-01-01  Andreas Scherer  <https://ascherer.github.io>
 
 	* ctwill.man: CWEB 4.12.1 release.
diff --git a/source/texk/web2c/synctexdir/ChangeLog b/source/texk/web2c/synctexdir/ChangeLog
index 64826d302..6e85d107f 100644
--- a/source/texk/web2c/synctexdir/ChangeLog
+++ b/source/texk/web2c/synctexdir/ChangeLog
@@ -1,3 +1,12 @@
+2025-03-09  Akira Kakuto  <kakuto@jcom.zaq.ne.jp>
+
+	* synctex.c: improve printing of a synctex file name
+	(windows only).
+
+2025-03-07  Karl Berry  <karl@tug.org>
+
+	* TL'25 release.
+
 2024-10-13  <user202729@protonmail.com>
 
 	* synctex_record_node_kern: pass "kern" instead of "glue"
diff --git a/source/texk/web2c/synctexdir/synctex.c b/source/texk/web2c/synctexdir/synctex.c
index af211af61..a439b3960 100644
--- a/source/texk/web2c/synctexdir/synctex.c
+++ b/source/texk/web2c/synctexdir/synctex.c
@@ -968,10 +968,11 @@ void synctexterminate(boolean log_opened)
                         if (SYNCTEX_interaction>0) {
 #ifdef W32UPTEXSYNCTEX
                         {
-                        char *stmp = chgto_oem(tmp);
+                        int savecp = GetConsoleOutputCP();
+                        SetConsoleOutputCP(file_system_codepage);
                         printf((synctex_ctxt.flags.quoted ? "\nSyncTeX written on \"%s\"\n" : "\nSyncTeX written on %s.\n"),
-                               stmp);
-                        free(stmp);
+                               tmp);
+                        SetConsoleOutputCP(savecp);
                         }
 #else
 #ifndef SYNCTEX_PRE_NL
diff --git a/source/texk/web2c/tangle.ch b/source/texk/web2c/tangle.ch
index acf898630..8ce847bb3 100644
--- a/source/texk/web2c/tangle.ch
+++ b/source/texk/web2c/tangle.ch
@@ -295,7 +295,8 @@ the replacement text.
     begin if buffer[i]>="a" then chopped_id[s]:=buffer[i]-@'40
 @y
   begin if (buffer[i]<>"_") or (allow_underlines and not strict_mode) then
-    begin if (strict_mode or force_uppercase) and (buffer[i]>="a") then
+    begin if (strict_mode or force_uppercase)
+           and (buffer[i]>="a") and (buffer[i]<="z") then
       chopped_id[s]:=buffer[i]-@'40
     else if (not strict_mode and force_lowercase)
            and (buffer[i]>="A") and (buffer[i]<="Z") then
@@ -313,7 +314,9 @@ else @<Define \(and output a new string of the pool@>;
     begin if c>="a" then c:=c-@'40; {merge lowercase with uppercase}
 @y
   if c<>"_" or (allow_underlines and not strict_mode) then
-    begin if (strict_mode or force_uppercase) and (c>="a") then c:=c-@'40
+    begin if (strict_mode or force_uppercase)
+           and (c>="a") and (c<="z") then
+      c:=c-@'40
     else if (not strict_mode and force_lowercase)
            and (c>="A") and (c<="Z") then
       c:=c+@'40;
@@ -482,9 +485,11 @@ with underlines removed. Extremely long identifiers must be chopped.
 identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
   while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
     begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
-    if force_uppercase and (out_contrib[k]>="a") then
+    if force_uppercase
+          and (out_contrib[k]>="a") and (out_contrib[k]<="z") then
       out_contrib[k]:=out_contrib[k]-@'40
-    else if force_lowercase and (out_contrib[k]<="Z") then
+    else if force_lowercase
+          and (out_contrib[k]>="A") and (out_contrib[k]<="Z") then
       out_contrib[k]:=out_contrib[k]+@'40
     else if not allow_underlines and (out_contrib[k]="_") then decr(k);
     end;
@@ -568,6 +573,12 @@ equiv[p]:=accumulator+@'10000000000; {name |p| now is defined to equal |accumula
     end
 @z
 
+@x [16.173] l.3095 - Reject strings as macro names.
+  if next_control<>identifier then
+@y
+  if (next_control<>identifier) or (buffer[id_first]="""") then
+@z
+
 @x [16.173] l.3107 - Add parametric2 macros (macros that use [] to delimit arguments).
   else @<If the next text is `\.{(\#)==}', call |define_macro|
     and |goto continue|@>;
diff --git a/source/texk/web2c/tangleboot.pin b/source/texk/web2c/tangleboot.pin
index ed0acc4d9..a829f257d 100644
--- a/source/texk/web2c/tangleboot.pin
+++ b/source/texk/web2c/tangleboot.pin
@@ -197,11 +197,11 @@ if(p=nameptr)or(t<>0)then{57:}begin if((p<>nameptr)and(t<>0)and(ilk[p]=0
 idfirst;s:=0;h:=0;
 while(i<idloc)and(s<unambiglength)do begin if(buffer[i]<>95)or(
 allowunderlines and not strictmode)then begin if(strictmode or
-forceuppercase)and(buffer[i]>=97)then choppedid[s]:=buffer[i]-32 else if
-(not strictmode and forcelowercase)and(buffer[i]>=65)and(buffer[i]<=90)
-then choppedid[s]:=buffer[i]+32 else choppedid[s]:=buffer[i];
-h:=(h+h+choppedid[s])mod hashsize;s:=s+1;end;i:=i+1;end;choppedid[s]:=0;
-end{:58};
+forceuppercase)and(buffer[i]>=97)and(buffer[i]<=122)then choppedid[s]:=
+buffer[i]-32 else if(not strictmode and forcelowercase)and(buffer[i]>=65
+)and(buffer[i]<=90)then choppedid[s]:=buffer[i]+32 else choppedid[s]:=
+buffer[i];h:=(h+h+choppedid[s])mod hashsize;s:=s+1;end;i:=i+1;end;
+choppedid[s]:=0;end{:58};
 if p<>nameptr then{59:}begin if ilk[p]=0 then begin if t=1 then begin
 writeln(stdout);write(stdout,'! This identifier has already appeared');
 error;end;{60:}q:=chophash[h];
@@ -214,8 +214,8 @@ chophash[h];while q<>0 do begin{63:}begin k:=bytestart[q];s:=0;
 w:=q mod 3;
 while(k<bytestart[q+3])and(s<unambiglength)do begin c:=bytemem[w,k];
 if c<>95 or(allowunderlines and not strictmode)then begin if(strictmode
-or forceuppercase)and(c>=97)then c:=c-32 else if(not strictmode and
-forcelowercase)and(c>=65)and(c<=90)then c:=c+32;
+or forceuppercase)and(c>=97)and(c<=122)then c:=c-32 else if(not
+strictmode and forcelowercase)and(c>=65)and(c<=90)then c:=c+32;
 if choppedid[s]<>c then goto 32;s:=s+1;end;k:=k+1;end;
 if(k=bytestart[q+3])and(choppedid[s]<>0)then goto 32;
 begin writeln(stdout);write(stdout,'! Identifier conflict with ');end;
@@ -469,11 +469,11 @@ outcontrib[1]:=curchar;sendout(2,1);end;
 130:begin k:=0;j:=bytestart[curval];w:=curval mod 3;
 while(k<maxidlength)and(j<bytestart[curval+3])do begin k:=k+1;
 outcontrib[k]:=bytemem[w,j];j:=j+1;
-if forceuppercase and(outcontrib[k]>=97)then outcontrib[k]:=outcontrib[k
-]-32 else if forcelowercase and(outcontrib[k]<=90)then outcontrib[k]:=
-outcontrib[k]+32 else if not allowunderlines and(outcontrib[k]=95)then k
-:=k-1;end;sendout(2,k);end;
-{:116}{119:}48,49,50,51,52,53,54,55,56,57:begin n:=0;
+if forceuppercase and(outcontrib[k]>=97)and(outcontrib[k]<=122)then
+outcontrib[k]:=outcontrib[k]-32 else if forcelowercase and(outcontrib[k]
+>=65)and(outcontrib[k]<=90)then outcontrib[k]:=outcontrib[k]+32 else if
+not allowunderlines and(outcontrib[k]=95)then k:=k-1;end;sendout(2,k);
+end;{:116}{119:}48,49,50,51,52,53,54,55,56,57:begin n:=0;
 repeat curchar:=curchar-48;if n>=214748364 then begin writeln(stdout);
 write(stdout,'! Constant too big');error;end else n:=10*n+curchar;
 curchar:=getoutput;until(curchar>57)or(curchar<48);sendval(n);k:=0;
@@ -807,8 +807,8 @@ label 22,30,10;var p:namepointer;begin modulecount:=modulecount+1;
 while true do begin 22:while nextcontrol<=132 do begin nextcontrol:=
 skipahead;if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;
 end;end;if nextcontrol<>133 then goto 30;nextcontrol:=getnext;
-if nextcontrol<>130 then begin begin writeln(stdout);
-write(stdout,'! Definition flushed, must start with ',
+if(nextcontrol<>130)or(buffer[idfirst]=34)then begin begin writeln(
+stdout);write(stdout,'! Definition flushed, must start with ',
 'identifier of length > 1');error;end;goto 22;end;nextcontrol:=getnext;
 if nextcontrol=61 then begin scannumeric(idlookup(1));goto 22;
 end else if nextcontrol=30 then begin definemacro(2);goto 22;
diff --git a/source/texk/web2c/web2c/ChangeLog b/source/texk/web2c/web2c/ChangeLog
index 0990fd196..f6efef1eb 100644
--- a/source/texk/web2c/web2c/ChangeLog
+++ b/source/texk/web2c/web2c/ChangeLog
@@ -1,3 +1,7 @@
+2025-03-07  Karl Berry  <karl@tug.org>
+
+	* TL'25 release.
+
 2023-03-09  Karl Berry  <karl@tug.org>
 
 	* TL'23 release.
diff --git a/source/texk/web2c/web2c/configure b/source/texk/web2c/web2c/configure
index 9599d9a15..c24c87d5e 100755
--- a/source/texk/web2c/web2c/configure
+++ b/source/texk/web2c/web2c/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for Web2C Tools 2025.
+# Generated by GNU Autoconf 2.72 for Web2C Tools 2026/dev.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -614,8 +614,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='Web2C Tools'
 PACKAGE_TARNAME='web2c-tools'
-PACKAGE_VERSION='2025'
-PACKAGE_STRING='Web2C Tools 2025'
+PACKAGE_VERSION='2026/dev'
+PACKAGE_STRING='Web2C Tools 2026/dev'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1365,7 +1365,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 Web2C Tools 2025 to adapt to many kinds of systems.
+'configure' configures Web2C Tools 2026/dev to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1436,7 +1436,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Web2C Tools 2025:";;
+     short | recursive ) echo "Configuration of Web2C Tools 2026/dev:";;
    esac
   cat <<\_ACEOF
 
@@ -1560,7 +1560,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Web2C Tools configure 2025
+Web2C Tools configure 2026/dev
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -2341,7 +2341,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 Web2C Tools $as_me 2025, which was
+It was created by Web2C Tools $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -9233,7 +9233,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='web2c-tools'
- VERSION='2025'
+ VERSION='2026/dev'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -16840,7 +16840,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 Web2C Tools $as_me 2025, which was
+This file was extended by Web2C Tools $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -16908,7 +16908,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="\\
-Web2C Tools config.status 2025
+Web2C Tools config.status 2026/dev
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/utils/README b/source/utils/README
index ef098a8fd..a2748f2e6 100644
--- a/source/utils/README
+++ b/source/utils/README
@@ -1,4 +1,4 @@
-$Id: README 74242 2025-02-23 23:13:34Z karl $
+$Id: README 74435 2025-03-04 17:01:16Z karl $
 Public domain.  Originally written 2005 by Karl Berry.
 
 Extra utilities we (optionally) compile for TeX Live.
@@ -9,6 +9,11 @@ asymptote 3.01 - checked 24feb25
   See https://tug.org/texlive/build.html#asymptote
   and tlpkg/bin/tl-update-asy
 
+  Special builders for asy:
+    aarch64-linux - Johannes Hielscher
+    *solaris, as well as darwinlegacy - Mojca
+    windows - from asy dist, including dlls
+
 autosp 2023-10-07 - checked 07jan24
   https://ctan.org/pkg/autosp
 
diff --git a/source/utils/configure b/source/utils/configure
index c1a09ac48..6f70f9058 100755
--- a/source/utils/configure
+++ b/source/utils/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for TeX Live utils 2025.
+# Generated by GNU Autoconf 2.72 for TeX Live utils 2026/dev.
 #
 # Report bugs to <tex-k@tug.org>.
 #
@@ -603,8 +603,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='TeX Live utils'
 PACKAGE_TARNAME='tex-live-utils'
-PACKAGE_VERSION='2025'
-PACKAGE_STRING='TeX Live utils 2025'
+PACKAGE_VERSION='2026/dev'
+PACKAGE_STRING='TeX Live utils 2026/dev'
 PACKAGE_BUGREPORT='tex-k@tug.org'
 PACKAGE_URL=''
 
@@ -1382,7 +1382,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 TeX Live utils 2025 to adapt to many kinds of systems.
+'configure' configures TeX Live utils 2026/dev to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1453,7 +1453,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of TeX Live utils 2025:";;
+     short | recursive ) echo "Configuration of TeX Live utils 2026/dev:";;
    esac
   cat <<\_ACEOF
 
@@ -1639,7 +1639,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-TeX Live utils configure 2025
+TeX Live utils configure 2026/dev
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -1799,7 +1799,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 TeX Live utils $as_me 2025, which was
+It was created by TeX Live utils $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -5776,7 +5776,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='tex-live-utils'
- VERSION='2025'
+ VERSION='2026/dev'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -6689,7 +6689,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 TeX Live utils $as_me 2025, which was
+This file was extended by TeX Live utils $as_me 2026/dev, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6748,7 +6748,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="\\
-TeX Live utils config.status 2025
+TeX Live utils config.status 2026/dev
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/source/version.ac b/source/version.ac
index df9f991a0..2ec2e79b3 100644
--- a/source/version.ac
+++ b/source/version.ac
@@ -1,4 +1,4 @@
-dnl $Id: version.ac 73591 2025-01-25 18:41:15Z karl $
+dnl $Id: version.ac 74529 2025-03-08 18:17:45Z karl $
 dnl   Copyright 2016-2025 Karl Berry <tex-live@tug.org>
 dnl   Copyright 2010-2015 Peter Breitenlohner <tex-live@tug.org>
 dnl
@@ -9,4 +9,4 @@ dnl
 dnl --------------------------------------------------------
 dnl
 dnl  m4-include this file to define the current TeX Live version
-m4_define([tex_live_version], [2025])
+m4_define([tex_live_version], [2026/dev])
-- 
GitLab