[autofit] Enable dynamic loading of HarfBuzz. (1/2)
This commit activates the mini-HarfBuzz header files and provides the
necessary infrastructure for dynamically loading HarfBuzz if
`FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC` is defined (this macro gets set up
in a follow-up commit).
* src/autofit/ft-hb.c: New file, providing `ft_hb_funcs_init` and
`ft_hb_funcs_done` for loading HarfBuzz dynamically. The name of the
library is hold in the macro `FT_LIBHARFBUZZ`, which can be overridden.
* src/autofit/ft-hb.h: Don't include `hb.h` but `ft-hb-types.h`.
(hb): Modified to handle both standard linking and dynamically
loading of HarfBuzz.
(HB_EXTERN): New macro to load `ft-hb-decls.h`.
* src/autofit/afadjust.c [FT_CONFIG_OPTION_USE_HARFBUZZ]: For the sake of
dynamically loading the HarfBuzz library, replace the compile-time macro
`HB_VERSION_ATLEAST` with a call to the run-time function
`hb_version_atleast` where necessary – a follow-up commit will set the
minimum version of HarfBuzz to 2.6.8, which provides all necessary
functions needed by FreeType.
* src/autofit/afmodule.h: Include `ft-hb.h`.
(AF_ModuleRec) [FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC]: Add `hb_funcs`
structure to hold pointers to the dynamically loaded HarfBuzz functions.
* src/autofit/afmodule.c (af_autofitter_init, af_autofitter_done)
[FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC]: Call `ft_hb_funcs_init` and
`ft_hb_funcs_done`.
* src/autofit/afshaper.h: Updated.
* src/autofit/autofit.c: Include `ft-hb.c`.
* src/autofit/rules.mk (AUTOF_DRV_SRC, AUTOF_DRV_H): Updated.
diff --git a/src/autofit/afadjust.c b/src/autofit/afadjust.c
index 6e9f5f7..9e1b807 100644
--- a/src/autofit/afadjust.c
+++ b/src/autofit/afadjust.c
@@ -987,7 +987,6 @@
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
-# if HB_VERSION_ATLEAST( 7, 2, 0 )
/*
Find all glyphs that a code point could turn into from the OpenType
@@ -1124,7 +1123,6 @@
hb( set_destroy )( helper_result );
}
-# endif /* HB_VERSION_ATLEAST */
#endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */
@@ -1166,8 +1164,8 @@
goto Exit;
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
-# if HB_VERSION_ATLEAST( 7, 2, 0 )
+ if ( hb( version_atleast )( 7, 2, 0 ) )
{
/* No need to check whether HarfBuzz has allocation issues; */
/* it continues to work in such cases and simply returns */
@@ -1277,10 +1275,9 @@
codepoint = FT_Get_Next_Char( face, codepoint, &glyph_index );
}
}
+ else
-# endif /* HB_VERSION_ATLEAST */
-
-#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
+#endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */
{
FT_UInt i;
@@ -1313,8 +1310,6 @@
af_reverse_character_map_entry_compare );
}
-#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
-
FT_TRACE4(( " reverse character map built successfully"
" with %ld entries\n", ( *map )->length ));
diff --git a/src/autofit/afmodule.c b/src/autofit/afmodule.c
index 726f6ca..9c1f2c2 100644
--- a/src/autofit/afmodule.c
+++ b/src/autofit/afmodule.c
@@ -412,6 +412,11 @@
module->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
module->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
+#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \
+ defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC )
+ ft_hb_funcs_init( module );
+#endif
+
return FT_Err_Ok;
}
@@ -421,6 +426,11 @@
{
FT_UNUSED( ft_module );
+#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \
+ defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC )
+ ft_hb_funcs_done( (AF_Module)ft_module );
+#endif
+
#ifdef FT_DEBUG_AUTOFIT
if ( af_debug_hints_rec_->memory )
af_glyph_hints_done( af_debug_hints_rec_ );
diff --git a/src/autofit/afmodule.h b/src/autofit/afmodule.h
index 91a1abf..f5a406c 100644
--- a/src/autofit/afmodule.h
+++ b/src/autofit/afmodule.h
@@ -22,6 +22,7 @@
#include <freetype/internal/ftobjs.h>
#include <freetype/ftmodapi.h>
+#include "ft-hb.h"
FT_BEGIN_HEADER
@@ -40,6 +41,11 @@
FT_Bool no_stem_darkening;
FT_Int darken_params[8];
+#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \
+ defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC )
+ ft_hb_funcs_t* hb_funcs;
+#endif
+
} AF_ModuleRec, *AF_Module;
diff --git a/src/autofit/afshaper.h b/src/autofit/afshaper.h
index f729953..4a4176f 100644
--- a/src/autofit/afshaper.h
+++ b/src/autofit/afshaper.h
@@ -23,15 +23,6 @@
#include <freetype/freetype.h>
-#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
-
-#include <hb.h>
-#include <hb-ot.h>
-#include "ft-hb-ft.h"
-
-#endif
-
-
FT_BEGIN_HEADER
FT_Error
diff --git a/src/autofit/autofit.c b/src/autofit/autofit.c
index a283e69..e7da68c 100644
--- a/src/autofit/autofit.c
+++ b/src/autofit/autofit.c
@@ -18,6 +18,7 @@
#define FT_MAKE_OPTION_SINGLE_OBJECT
+#include "ft-hb.c"
#include "ft-hb-ft.c"
#include "afadjust.c"
#include "afblue.c"
diff --git a/src/autofit/ft-hb.c b/src/autofit/ft-hb.c
new file mode 100644
index 0000000..110d7bf
--- /dev/null
+++ b/src/autofit/ft-hb.c
@@ -0,0 +1,165 @@
+/****************************************************************************
+ *
+ * ft-hb.c
+ *
+ * FreeType-HarfBuzz bridge (body).
+ *
+ * Copyright (C) 2025 by
+ * Behdad Esfahbod.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#include <freetype/freetype.h>
+#include <freetype/internal/ftmemory.h>
+
+#include "afglobal.h"
+
+#include "ft-hb.h"
+
+
+#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \
+ defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC )
+
+#ifndef FT_LIBHARFBUZZ
+# ifdef _WIN32
+# define FT_LIBHARFBUZZ "libharfbuzz-0.dll"
+# else
+# ifdef __APPLE__
+# define FT_LIBHARFBUZZ "libharfbuzz.0.dylib"
+# else
+# define FT_LIBHARFBUZZ "libharfbuzz.so.0"
+# endif
+# endif
+#endif
+
+#ifdef _WIN32
+
+# include <windows.h>
+
+#else /* !_WIN32 */
+
+# include <dlfcn.h>
+
+ /* The GCC pragma suppresses the warning "ISO C forbids */
+ /* assignment between function pointer and 'void *'", which */
+ /* inevitably gets emitted with `-Wpedantic`; see the man */
+ /* page of function `dlsym` for more information. */
+# if defined( __GNUC__ )
+# pragma GCC diagnostic push
+# ifndef __cplusplus
+# pragma GCC diagnostic ignored "-Wpedantic"
+# endif
+# endif
+
+#endif /* !_WIN32 */
+
+
+ FT_LOCAL_DEF( void )
+ ft_hb_funcs_init( struct AF_ModuleRec_ *af_module )
+ {
+ FT_Memory memory = af_module->root.memory;
+ FT_Error error;
+
+ ft_hb_funcs_t *funcs = NULL;
+ ft_hb_version_atleast_func_t version_atleast = NULL;
+
+#ifdef _WIN32
+ HANDLE lib;
+# define DLSYM( lib, name ) \
+ (ft_ ## name ## _func_t)GetProcAddress( lib, #name )
+#else
+ void *lib;
+# define DLSYM( lib, name ) \
+ (ft_ ## name ## _func_t)dlsym( lib, #name )
+#endif
+
+
+ af_module->hb_funcs = NULL;
+
+ if ( FT_NEW( funcs ) )
+ return;
+ FT_ZERO( funcs );
+
+#ifdef _WIN32
+
+ lib = LoadLibraryA( FT_LIBHARFBUZZ );
+ if ( !lib )
+ goto Fail;
+ version_atleast = DLSYM( lib, hb_version_atleast );
+
+#else /* !_WIN32 */
+
+ lib = RTLD_DEFAULT;
+ version_atleast = DLSYM( lib, hb_version_atleast );
+ if ( !version_atleast )
+ {
+ /* Load the HarfBuzz library.
+ *
+ * We never close the library, since we opened it with RTLD_GLOBAL.
+ * This is important for the case where we are using HarfBuzz as a
+ * shared library, and we want to use the symbols from the library in
+ * other shared libraries or clients. HarfBuzz holds onto global
+ * variables, and closing the library will cause them to be
+ * invalidated.
+ */
+ lib = dlopen( FT_LIBHARFBUZZ, RTLD_LAZY | RTLD_GLOBAL );
+ if ( !lib )
+ goto Fail;
+ version_atleast = DLSYM( lib, hb_version_atleast );
+ if ( !version_atleast )
+ goto Fail;
+ }
+
+#endif /* !_WIN32 */
+
+ /* Load all symbols we use. */
+#define HB_EXTERN( ret, name, args ) \
+ { \
+ funcs->name = DLSYM( lib, name ); \
+ if ( !funcs->name ) \
+ goto Fail; \
+ }
+#include "ft-hb-decls.h"
+#undef HB_EXTERN
+
+#undef DLSYM
+
+ af_module->hb_funcs = funcs;
+ return;
+
+ Fail:
+ if ( funcs )
+ FT_FREE( funcs );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ft_hb_funcs_done( struct AF_ModuleRec_ *af_module )
+ {
+ FT_Memory memory = af_module->root.memory;
+
+
+ if ( af_module->hb_funcs )
+ {
+ FT_FREE( af_module->hb_funcs );
+ af_module->hb_funcs = NULL;
+ }
+ }
+
+#ifndef _WIN32
+# if defined( __GNUC__ )
+# pragma GCC diagnostic pop
+# endif
+#endif
+
+#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */
+
+
+/* END */
diff --git a/src/autofit/ft-hb.h b/src/autofit/ft-hb.h
index 05e8df5..399bfa9 100644
--- a/src/autofit/ft-hb.h
+++ b/src/autofit/ft-hb.h
@@ -19,8 +19,6 @@
#ifndef FT_HB_H
#define FT_HB_H
-#include <hb.h>
-
#include <freetype/internal/compiler-macros.h>
#include <freetype/freetype.h>
@@ -29,10 +27,47 @@
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
-#define hb( x ) hb_ ## x
+# include "ft-hb-types.h"
+
+# ifdef FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC
+
+# define HB_EXTERN( ret, name, args ) \
+ typedef ret (*ft_ ## name ## _func_t) args;
+# include "ft-hb-decls.h"
+# undef HB_EXTERN
+
+ typedef struct ft_hb_funcs_t
+ {
+# define HB_EXTERN( ret, name, args ) \
+ ft_ ## name ## _func_t name;
+# include "ft-hb-decls.h"
+# undef HB_EXTERN
+ } ft_hb_funcs_t;
+
+ struct AF_ModuleRec_;
+
+ FT_LOCAL( void )
+ ft_hb_funcs_init( struct AF_ModuleRec_ *af_module );
+
+ FT_LOCAL( void )
+ ft_hb_funcs_done( struct AF_ModuleRec_ *af_module );
+
+# define hb( x ) globals->module->hb_funcs->hb_ ## x
+
+# else /* !FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */
+
+# define HB_EXTERN( ret, name, args ) \
+ ret name args;
+# include "ft-hb-decls.h"
+# undef HB_EXTERN
+
+# define hb( x ) hb_ ## x
+
+# endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */
#endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */
+
FT_END_HEADER
#endif /* FT_HB_H */
diff --git a/src/autofit/rules.mk b/src/autofit/rules.mk
index b8766d3..c10288f 100644
--- a/src/autofit/rules.mk
+++ b/src/autofit/rules.mk
@@ -40,18 +40,20 @@
$(AUTOF_DIR)/afmodule.c \
$(AUTOF_DIR)/afranges.c \
$(AUTOF_DIR)/afshaper.c \
+ $(AUTOF_DIR)/ft-hb.c \
$(AUTOF_DIR)/ft-hb-ft.c
# AUTOF driver headers
#
-AUTOF_DRV_H := $(AUTOF_DRV_SRC:%c=%h) \
- $(AUTOF_DIR)/afcover.h \
- $(AUTOF_DIR)/aferrors.h \
- $(AUTOF_DIR)/afscript.h \
- $(AUTOF_DIR)/afstyles.h \
- $(AUTOF_DIR)/aftypes.h \
- $(AUTOF_DIR)/afws-decl.h \
- $(AUTOF_DIR)/afws-iter.h
+AUTOF_DRV_H := $(AUTOF_DRV_SRC:%c=%h) \
+ $(AUTOF_DIR)/afcover.h \
+ $(AUTOF_DIR)/aferrors.h \
+ $(AUTOF_DIR)/afscript.h \
+ $(AUTOF_DIR)/afstyles.h \
+ $(AUTOF_DIR)/aftypes.h \
+ $(AUTOF_DIR)/afws-decl.h \
+ $(AUTOF_DIR)/afws-iter.h \
+ $(AUTOF_DIR)/ft-hb-decls.h
# AUTOF driver object(s)