From 842a19fff66c5e8e501c658a7b66cae4fbe762c8 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Thu, 27 Aug 2026 13:05:34 +0300 Subject: [PATCH 1/3] Wheels: link FreeType against the bundled HarfBuzz FreeType's autofitter can use HarfBuzz to work out which glyphs a script covers, so it can derive blue zones for scripts such as Arabic. Only the Windows wheels were built to allow that. macOS passed --with-harfbuzz=no, and Linux left it unconfigured, which meant FreeType defaulted to `dlopen`ing HarfBuzz at runtime with the name "libharfbuzz.so.0". `auditwheel`, part of the wheeling process, renames shared libraries to avoid conflicts, so the Linux wheels ended up with a FreeType that would only find HarfBuzz if the system had its own copy with the name "libharfbuzz.so.0"; the bundled copy was never used. --- .github/workflows/wheels-dependencies.sh | 20 ++++++++++++++------ docs/releasenotes/13.0.0.rst | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index b3a5782091b..9be2867dfb8 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -159,6 +159,12 @@ function build_brotli { touch brotli-stamp } +function build_freetype { # overrides multibuild's function so we can add "$@" args + build_libpng + build_bzip2 + build_simple freetype $FREETYPE_VERSION https://download.savannah.gnu.org/releases/freetype tar.gz "$@" +} + function build_harfbuzz { if [ -e harfbuzz-stamp ]; then return; fi python3 -m pip install meson ninja @@ -304,17 +310,19 @@ function build { build_brotli - if [[ -n "$IS_MACOS" ]]; then - # Custom freetype build - build_simple freetype $FREETYPE_VERSION https://download.savannah.gnu.org/releases/freetype tar.gz --with-harfbuzz=no - else - build_freetype - fi + # FreeType and HarfBuzz each want the other: + # HarfBuzz reads font data through FreeType, and FreeType's autofitter asks HarfBuzz which glyphs a script covers. + # Break the cycle by building FreeType twice, so that the FreeType we ship is linked against the HarfBuzz we ship. + build_freetype --with-harfbuzz=no if [[ -z "$IOS_SDK" ]]; then # On iOS, there's no vendor-provided raqm, and we can't ship it due to # licensing, so there's no point building harfbuzz. build_harfbuzz + + # Now that HarfBuzz exists, build FreeType again against it. + rm -rf freetype-$FREETYPE_VERSION freetype-stamp + build_freetype --with-harfbuzz=yes fi } diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index 4db2f496701..1a1b91750cb 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -97,6 +97,25 @@ that share a local name across different namespaces. Other changes ============= +Wheels now use HarfBuzz for hinting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +FreeType can use HarfBuzz to work out which glyphs a script covers, +so that its autofitter can derive hinting "Blue Zones" for scripts such as Arabic. + +Only the Windows wheels were actually built with this support. +The macOS wheels disabled HarfBuzz intreop outright, +and the Linux wheels left it up to FreeType's ``configure``, +which in turn fell back to loading HarfBuzz by its unversioned name at +runtime. + +Since the bundled copy of HarfBuzz is renamed when the wheel is audited and repaired +for distribution, that lookup only succeeded if a separate copy happened to be +installed on the system with the expected name (e.g. ``libharfbuzz.so.0``). + +All wheels are now linked against the HarfBuzz that Pillow ships, +which may slightly change how text is rendered in scripts that rely on the autofitter. + Python 3.15 ^^^^^^^^^^^ From 4e89b3b327a16de5e93eb5ef1a0ad333802969d2 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 15 Sep 2026 05:37:00 +1000 Subject: [PATCH 2/3] Use FT_CONFIG_OPTION_USE_HARFBUZZ Co-authored-by: Andrew Murray --- .github/workflows/wheels-dependencies.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 9be2867dfb8..4b324ad3840 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -159,12 +159,6 @@ function build_brotli { touch brotli-stamp } -function build_freetype { # overrides multibuild's function so we can add "$@" args - build_libpng - build_bzip2 - build_simple freetype $FREETYPE_VERSION https://download.savannah.gnu.org/releases/freetype tar.gz "$@" -} - function build_harfbuzz { if [ -e harfbuzz-stamp ]; then return; fi python3 -m pip install meson ninja @@ -313,7 +307,7 @@ function build { # FreeType and HarfBuzz each want the other: # HarfBuzz reads font data through FreeType, and FreeType's autofitter asks HarfBuzz which glyphs a script covers. # Break the cycle by building FreeType twice, so that the FreeType we ship is linked against the HarfBuzz we ship. - build_freetype --with-harfbuzz=no + build_freetype if [[ -z "$IOS_SDK" ]]; then # On iOS, there's no vendor-provided raqm, and we can't ship it due to @@ -322,7 +316,7 @@ function build { # Now that HarfBuzz exists, build FreeType again against it. rm -rf freetype-$FREETYPE_VERSION freetype-stamp - build_freetype --with-harfbuzz=yes + CFLAGS="$CFLAGS -DFT_CONFIG_OPTION_USE_HARFBUZZ" build_freetype fi } From 6ab689cc564a95648558d1933e99067660ecf277 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:11:05 +1000 Subject: [PATCH 3/3] Refer to auto-hinter in comments Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .github/workflows/wheels-dependencies.sh | 2 +- docs/releasenotes/13.0.0.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 4b324ad3840..2c28199051a 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -305,7 +305,7 @@ function build { build_brotli # FreeType and HarfBuzz each want the other: - # HarfBuzz reads font data through FreeType, and FreeType's autofitter asks HarfBuzz which glyphs a script covers. + # HarfBuzz reads font data through FreeType, and FreeType's auto-hinter asks HarfBuzz which glyphs a script covers. # Break the cycle by building FreeType twice, so that the FreeType we ship is linked against the HarfBuzz we ship. build_freetype diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index 1a1b91750cb..ad1ccee89cc 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -101,10 +101,10 @@ Wheels now use HarfBuzz for hinting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FreeType can use HarfBuzz to work out which glyphs a script covers, -so that its autofitter can derive hinting "Blue Zones" for scripts such as Arabic. +so that its auto-hinter can derive hinting "Blue Zones" for scripts such as Arabic. Only the Windows wheels were actually built with this support. -The macOS wheels disabled HarfBuzz intreop outright, +The macOS wheels disabled HarfBuzz interoperability outright, and the Linux wheels left it up to FreeType's ``configure``, which in turn fell back to loading HarfBuzz by its unversioned name at runtime. @@ -114,7 +114,7 @@ for distribution, that lookup only succeeded if a separate copy happened to be installed on the system with the expected name (e.g. ``libharfbuzz.so.0``). All wheels are now linked against the HarfBuzz that Pillow ships, -which may slightly change how text is rendered in scripts that rely on the autofitter. +which may slightly change how text is rendered in scripts that rely on the auto-hinter. Python 3.15 ^^^^^^^^^^^