From fba3687db414306da780d1faeb13fe947d2e6da6 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 31 May 2019 11:30:36 +0300 Subject: [PATCH] Add HarfBuzz font backend for MS-Windows * src/w32uniscribe.c [HAVE_HARFBUZZ]: Include math.h and hb.h. (bswap_32): Define for GCC 4.3.0 and later; else include from Gnulib. (struct uniscribe_font_info): Extend for HarfBuzz; 'cache' is now a 'void *' (all users changed). [HAVE_HARFBUZZ]: Define typedefs for HarfBuzz functions to be loaded dynamically from the HarfBuzz DLL. Define macros to call those functions via function pointers. (uniscribe_open) [HAVE_HARFBUZZ]: Use the HarfBuzz font driver if the type of the font entity is 'harfbuzz'. (uniscribe_close) [HAVE_HARFBUZZ]: For fonts using the HarfBuzz backend, call hb_font_destroy to free memory used for the cached hb_font data. (uniscribe_shape): Fix assignment of character codepoints to glyphs from a single cluster. (w32hb_list, w32hb_match, free_cb, w32hb_get_font_table) (w32hb_get_font, w32hb_encode_char, w32hb_begin_font) (w32uni_combining, w32uni_general, w32uni_mirroring) (get_hb_unicode_funcs, w32hb_shape) (w32hb_combining_capability, load_harfbuzz_funcs) [HAVE_HARFBUZZ]: New functions. (syms_of_w32uniscribe_for_pdumper) [HAVE_HARFBUZZ]: Load the HarfBuzz DLL and register the HarfBuzz backend with its functions. * src/w32font.c (syms_of_w32font) : New DEFSYM. * src/w32fns.c (Fx_create_frame, w32_create_tip_frame) [HAVE_HARFBUZZ]: Register the harfbuzz font backend. * src/lisp.h (get_unicode_property): Declare prototype. * src/font.h (harfbuzz_font_driver) [HAVE_NTGUI]: Declare. * src/chartab.c (get_unicode_property): New function, body taken from get-unicode-property-internal. (Fget_unicode_property_internal): Call get_unicode_property after validating input. * doc/lispref/frames.texi (Font and Color Parameters): * doc/emacs/msdos.texi (Windows Fonts): Document support for HarfBuzz text shaping on MS-Windows. * configure.ac (HAVE_HARFBUZZ): Move out of the X-specific part, and consider HarfBuzz also for HAVE_W32 systems. Require HarfBuzz v1.2.3 for w32. --- configure.ac | 94 ++--- doc/emacs/msdos.texi | 37 +- doc/lispref/frames.texi | 27 +- src/chartab.c | 15 +- src/font.h | 3 + src/lisp.h | 1 + src/w32fns.c | 9 + src/w32font.c | 1 + src/w32uniscribe.c | 742 ++++++++++++++++++++++++++++++++++++++-- 9 files changed, 831 insertions(+), 98 deletions(-) diff --git a/configure.ac b/configure.ac index e9001fd1b7a..ab6b03f5462 100644 --- a/configure.ac +++ b/configure.ac @@ -3409,56 +3409,70 @@ if test "${HAVE_X11}" = "yes"; then fi fi # $HAVE_CAIRO != yes - HAVE_HARFBUZZ=no - HAVE_LIBOTF=no - if test "${HAVE_FREETYPE}" = "yes"; then - AC_DEFINE(HAVE_FREETYPE, 1, - [Define to 1 if using the freetype and fontconfig libraries.]) - if test "${with_harfbuzz}" != "no"; then - EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= 0.9.42]) - if test "$HAVE_HARFBUZZ" = "yes"; then - AC_DEFINE(HAVE_HARFBUZZ, 1, [Define to 1 if using HarfBuzz.]) + HAVE_LIBOTF=no + if test "${HAVE_FREETYPE}" = "yes"; then + AC_DEFINE(HAVE_FREETYPE, 1, + [Define to 1 if using the freetype and fontconfig libraries.]) + if test "${with_libotf}" != "no"; then + EMACS_CHECK_MODULES([LIBOTF], [libotf]) + if test "$HAVE_LIBOTF" = "yes"; then + AC_DEFINE(HAVE_LIBOTF, 1, [Define to 1 if using libotf.]) + AC_CHECK_LIB(otf, OTF_get_variation_glyphs, + HAVE_OTF_GET_VARIATION_GLYPHS=yes, + HAVE_OTF_GET_VARIATION_GLYPHS=no) + if test "${HAVE_OTF_GET_VARIATION_GLYPHS}" = "yes"; then + AC_DEFINE(HAVE_OTF_GET_VARIATION_GLYPHS, 1, + [Define to 1 if libotf has OTF_get_variation_glyphs.]) fi - fi - if test "${with_libotf}" != "no"; then - EMACS_CHECK_MODULES([LIBOTF], [libotf]) - if test "$HAVE_LIBOTF" = "yes"; then - AC_DEFINE(HAVE_LIBOTF, 1, [Define to 1 if using libotf.]) - AC_CHECK_LIB(otf, OTF_get_variation_glyphs, - HAVE_OTF_GET_VARIATION_GLYPHS=yes, - HAVE_OTF_GET_VARIATION_GLYPHS=no) - if test "${HAVE_OTF_GET_VARIATION_GLYPHS}" = "yes"; then - AC_DEFINE(HAVE_OTF_GET_VARIATION_GLYPHS, 1, - [Define to 1 if libotf has OTF_get_variation_glyphs.]) - fi - if ! $PKG_CONFIG --atleast-version=0.9.16 libotf; then - AC_DEFINE(HAVE_OTF_KANNADA_BUG, 1, + if ! $PKG_CONFIG --atleast-version=0.9.16 libotf; then + AC_DEFINE(HAVE_OTF_KANNADA_BUG, 1, [Define to 1 if libotf is affected by https://debbugs.gnu.org/28110.]) - fi fi fi - dnl FIXME should there be an error if HAVE_FREETYPE != yes? - dnl Does the new font backend require it, or can it work without it? fi + dnl FIXME should there be an error if HAVE_FREETYPE != yes? + dnl Does the new font backend require it, or can it work without it? + fi - HAVE_M17N_FLT=no - if test "${HAVE_LIBOTF}" = yes; then - if test "${with_m17n_flt}" != "no"; then - EMACS_CHECK_MODULES([M17N_FLT], [m17n-flt]) - if test "$HAVE_M17N_FLT" = "yes"; then - AC_DEFINE(HAVE_M17N_FLT, 1, [Define to 1 if using libm17n-flt.]) - fi + HAVE_M17N_FLT=no + if test "${HAVE_LIBOTF}" = yes; then + if test "${with_m17n_flt}" != "no"; then + EMACS_CHECK_MODULES([M17N_FLT], [m17n-flt]) + if test "$HAVE_M17N_FLT" = "yes"; then + AC_DEFINE(HAVE_M17N_FLT, 1, [Define to 1 if using libm17n-flt.]) fi fi -else - HAVE_XFT=no - HAVE_FREETYPE=no - HAVE_HARFBUZZ=no - HAVE_LIBOTF=no - HAVE_M17N_FLT=no + fi +else # "${HAVE_X11}" != "yes" + HAVE_XFT=no + HAVE_FREETYPE=no + HAVE_LIBOTF=no + HAVE_M17N_FLT=no +fi # "${HAVE_X11}" != "yes" + +HAVE_HARFBUZZ=no +if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \ + || test "${HAVE_W32}" = "yes"; then + if test "${with_harfbuzz}" != "no"; then + ### On MS-Windows we use hb_font_get_nominal_glyph, which appeared + ### in HarfBuzz version 1.2.3 + if test "${HAVE_W32}" = "yes"; then + EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= 1.2.3]) + else + EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= 0.9.42]) + fi + if test "$HAVE_HARFBUZZ" = "yes"; then + AC_DEFINE(HAVE_HARFBUZZ, 1, [Define to 1 if using HarfBuzz.]) + ### mingw32 and Cygwin-w32 don't use -lharfbuzz, since they load + ### the library dynamically. + if test "${HAVE_W32}" = "yes"; then + HARFBUZZ_LIBS= + fi + fi + fi fi -### End of font-backend (under X11) section. +### End of font-backend section. AC_SUBST(FREETYPE_CFLAGS) AC_SUBST(FREETYPE_LIBS) diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi index 9fc4b6262fb..27a7cd80968 100644 --- a/doc/emacs/msdos.texi +++ b/doc/emacs/msdos.texi @@ -985,21 +985,28 @@ fontconfig library used in modern Free desktops: The old XLFD based format is also supported for backwards compatibility. @cindex font backend selection (MS-Windows) - Emacs 23 and later supports a number of font backends. Currently, -the @code{gdi} and @code{uniscribe} backends are supported on Windows. -The @code{gdi} font backend is available on all versions of Windows, -and supports all fonts that are natively supported by Windows. The -@code{uniscribe} font backend is available on Windows 2000 and later, -and supports TrueType and OpenType fonts. Some languages requiring -complex layout can only be properly supported by the Uniscribe -backend. By default, both backends are enabled if supported, with -@code{uniscribe} taking priority over @code{gdi}. To override that -and use the GDI backend even if Uniscribe is available, invoke Emacs -with the @kbd{-xrm Emacs.fontBackend:gdi} command-line argument, or -add a @code{Emacs.fontBackend} resource with the value @code{gdi} in -the Registry under either the -@samp{HKEY_CURRENT_USER\SOFTWARE\GNU\Emacs} or the -@samp{HKEY_LOCAL_MACHINE\SOFTWARE\GNU\Emacs} key (@pxref{Resources}). + Emacs on MS-Windows supports a number of font backends. Currently, +the @code{gdi}, @code{uniscribe}, and @code{harfbuzz} backends are +available. The @code{gdi} font backend is available on all versions +of Windows, and supports all fonts that are natively supported by +Windows. The @code{uniscribe} font backend is available on Windows +2000 and later, and supports TrueType and OpenType fonts. The +@code{harfbuzz} font backend is available if Emacs was built with +HarfBuzz support, and if the HarfBuzz DLL is installed on your system; +like @code{uniscribe}, this backend supports only TrueType and +OpenType fonts. Some languages requiring complex layout can only be +properly supported by the Uniscribe or HarfBuzz backends. By default, +all backends are enabled if supported, with @code{harfbuzz} taking +priority over @code{uniscribe}, and @code{uniscribe} taking priority +over @code{gdi}. To override that and use the GDI backend even if +Uniscribe is available, invoke Emacs with the @kbd{-xrm +Emacs.fontBackend:gdi} command-line argument, or add a +@code{Emacs.fontBackend} resource with the value @code{gdi} in the +Registry under either the @samp{HKEY_CURRENT_USER\SOFTWARE\GNU\Emacs} +or the @samp{HKEY_LOCAL_MACHINE\SOFTWARE\GNU\Emacs} key +(@pxref{Resources}). Similarly, to use the Uniscribe backend even if +HarfBuzz is available, use @kbd{-xrm Emacs.fontBackend:uniscribe} on +the command line that invokes Emacs. @cindex font properties (MS Windows) @noindent diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index c94b4e67aed..336075a1ca2 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -2278,18 +2278,21 @@ variable do not take effect immediately, only when you specify the @vindex font-backend@r{, a frame parameter} @item font-backend A list of symbols, specifying the @dfn{font backends} to use for -drawing fonts in the frame, in order of priority. On X, there are -currently three available font backends if Emacs was built without the -Cairo drawing: @code{x} (the X core font driver), @code{xft} (the Xft -font driver), and @code{xfthb} (the Xft font driver with HarfBuzz text -shaping). If built with the Cairo drawing, then there are two -available font backends: @code{ftcr} (the FreeType font driver on -Cairo) and @code{ftcrhb} (the FreeType font driver on Cairo with -HarfBuzz text shaping). On MS-Windows, there are currently two -available font backends: @code{gdi} and @code{uniscribe} -(@pxref{Windows Fonts,,, emacs, The GNU Emacs Manual}). On other -systems, there is only one available font backend, so it does not make -sense to modify this frame parameter. +drawing characters on the frame, in order of priority. In Emacs built +without Cairo drawing on X, there are currently three available font +backends: @code{x} (the X core font driver), @code{xft} (the Xft font +driver), and @code{xfthb} (the Xft font driver with HarfBuzz text +shaping). If built with the Cairo drawing, there are two available +font backends on X: @code{ftcr} (the FreeType font driver on Cairo) +and @code{ftcrhb} (the FreeType font driver on Cairo with HarfBuzz +text shaping). On MS-Windows, there are currently three available +font backends: @code{gdi} (the core MS-Windows font driver), +@code{uniscribe} (font driver for OTF and TTF fonts with text shaping +by the Uniscribe engine), and @code{harfbuzz} (font driver for OTF and +TTF fonts with HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, +The GNU Emacs Manual}). On other systems, there is only one available +font backend, so it does not make sense to modify this frame +parameter. @vindex background-mode@r{, a frame parameter} @item background-mode diff --git a/src/chartab.c b/src/chartab.c index 16017f4a49a..bf8e34b2529 100644 --- a/src/chartab.c +++ b/src/chartab.c @@ -1321,22 +1321,25 @@ and put an element value. */) return Fcdr (Fassq (prop, Vchar_code_property_alist)); } +Lisp_Object +get_unicode_property (Lisp_Object char_table, int ch) +{ + Lisp_Object val = CHAR_TABLE_REF (char_table, ch); + uniprop_decoder_t decoder = uniprop_get_decoder (char_table); + return (decoder ? decoder (char_table, val) : val); +} + DEFUN ("get-unicode-property-internal", Fget_unicode_property_internal, Sget_unicode_property_internal, 2, 2, 0, doc: /* Return an element of CHAR-TABLE for character CH. CHAR-TABLE must be what returned by `unicode-property-table-internal'. */) (Lisp_Object char_table, Lisp_Object ch) { - Lisp_Object val; - uniprop_decoder_t decoder; - CHECK_CHAR_TABLE (char_table); CHECK_CHARACTER (ch); if (! UNIPROP_TABLE_P (char_table)) error ("Invalid Unicode property table"); - val = CHAR_TABLE_REF (char_table, XFIXNUM (ch)); - decoder = uniprop_get_decoder (char_table); - return (decoder ? decoder (char_table, val) : val); + return get_unicode_property (char_table, XFIXNUM (ch)); } DEFUN ("put-unicode-property-internal", Fput_unicode_property_internal, diff --git a/src/font.h b/src/font.h index a590bda3db4..4d1341a0db8 100644 --- a/src/font.h +++ b/src/font.h @@ -951,6 +951,9 @@ extern void syms_of_bdffont (void); #ifdef HAVE_NTGUI extern struct font_driver w32font_driver; extern struct font_driver uniscribe_font_driver; +#ifdef HAVE_HARFBUZZ +extern struct font_driver harfbuzz_font_driver; +#endif extern void syms_of_w32font (void); #endif /* HAVE_NTGUI */ #ifdef HAVE_NS diff --git a/src/lisp.h b/src/lisp.h index 6db90596899..5bd88f32e6e 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3994,6 +3994,7 @@ extern void map_char_table_for_charset (void (*c_function) (Lisp_Object, Lisp_Ob Lisp_Object, struct charset *, unsigned, unsigned); extern Lisp_Object uniprop_table (Lisp_Object); +extern Lisp_Object get_unicode_property (Lisp_Object, int); extern void syms_of_chartab (void); /* Defined in print.c. */ diff --git a/src/w32fns.c b/src/w32fns.c index bb74fcc1640..25fa1ac6ea0 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -221,6 +221,7 @@ int menubar_in_use = 0; /* From w32uniscribe.c */ extern void syms_of_w32uniscribe (void); extern int uniscribe_available; +extern int harfbuzz_available; #ifdef WINDOWSNT /* From w32inevt.c */ @@ -5843,6 +5844,10 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, specbind (Qx_resource_name, name); } +#ifdef HAVE_HARFBUZZ + if (harfbuzz_available) + register_font_driver (&harfbuzz_font_driver, f); +#endif if (uniscribe_available) register_font_driver (&uniscribe_font_driver, f); register_font_driver (&w32font_driver, f); @@ -6896,6 +6901,10 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) specbind (Qx_resource_name, name); } +#ifdef HAVE_HARFBUZZ + if (harfbuzz_available) + register_font_driver (&harfbuzz_font_driver, f); +#endif if (uniscribe_available) register_font_driver (&uniscribe_font_driver, f); register_font_driver (&w32font_driver, f); diff --git a/src/w32font.c b/src/w32font.c index bd68e22cc90..2576df64b6f 100644 --- a/src/w32font.c +++ b/src/w32font.c @@ -2634,6 +2634,7 @@ syms_of_w32font (void) { DEFSYM (Qgdi, "gdi"); DEFSYM (Quniscribe, "uniscribe"); + DEFSYM (Qharfbuzz, "harfbuzz"); DEFSYM (QCformat, ":format"); /* Generic font families. */ diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c index 693e4a4b1b3..f1e69452160 100644 --- a/src/w32uniscribe.c +++ b/src/w32uniscribe.c @@ -29,6 +29,15 @@ along with GNU Emacs. If not, see . */ #define _WIN32_WINNT 0x0600 #include #include +#ifdef HAVE_HARFBUZZ +# include /* for lround */ +# include +# if GNUC_PREREQ (4, 3, 0) +# define bswap_32(v) __builtin_bswap32(v) +# else +# include +# endif +#endif #include "lisp.h" #include "w32term.h" @@ -39,10 +48,16 @@ along with GNU Emacs. If not, see . */ #include "pdumper.h" #include "w32common.h" +/* Extension of w32font_info used by Uniscribe and HarfBuzz backends. */ struct uniscribe_font_info { struct w32font_info w32_font; - SCRIPT_CACHE cache; + /* This is used by the Uniscribe backend as a pointer to the script + cache, and by the HarfBuzz backend as a pointer to a hb_font_t + object. */ + void *cache; + /* This is used by the HarfBuzz backend to store the font scale. */ + double scale; }; int uniscribe_available = 0; @@ -51,6 +66,94 @@ int uniscribe_available = 0; static int CALLBACK ALIGN_STACK add_opentype_font_name_to_list (ENUMLOGFONTEX *, NEWTEXTMETRICEX *, DWORD, LPARAM); +#ifdef HAVE_HARFBUZZ + +struct font_driver harfbuzz_font_driver; +int harfbuzz_available = 0; + +/* Typedefs for HarfBuzz functions which we call through function + pointers initialized after we load the HarfBuzz DLL. */ +DEF_DLL_FN (hb_blob_t *, hb_blob_create, + (const char *, unsigned int, hb_memory_mode_t, void *, + hb_destroy_func_t)); +DEF_DLL_FN (hb_face_t *, hb_face_create_for_tables, + (hb_reference_table_func_t, void *, hb_destroy_func_t)); +DEF_DLL_FN (unsigned, hb_face_get_glyph_count, (const hb_face_t *)); +DEF_DLL_FN (hb_font_t *, hb_font_create, (hb_face_t *)); +DEF_DLL_FN (void, hb_font_destroy, (hb_font_t *)); +DEF_DLL_FN (void, hb_face_destroy, (hb_face_t *)); +DEF_DLL_FN (unsigned int, hb_face_get_upem, (hb_face_t *)); +DEF_DLL_FN (hb_bool_t, hb_font_get_nominal_glyph, + (hb_font_t *, hb_codepoint_t, hb_codepoint_t *)); +DEF_DLL_FN (hb_unicode_funcs_t *, hb_unicode_funcs_create, + (hb_unicode_funcs_t *)); +DEF_DLL_FN (hb_unicode_funcs_t *, hb_unicode_funcs_get_default, (void)); +DEF_DLL_FN (void, hb_unicode_funcs_set_combining_class_func, + (hb_unicode_funcs_t *, hb_unicode_combining_class_func_t, + void *, hb_destroy_func_t)); +DEF_DLL_FN (void, hb_unicode_funcs_set_general_category_func, + (hb_unicode_funcs_t *, hb_unicode_general_category_func_t, + void *, hb_destroy_func_t)); +DEF_DLL_FN (void, hb_unicode_funcs_set_mirroring_func, + (hb_unicode_funcs_t *, hb_unicode_mirroring_func_t, + void *, hb_destroy_func_t)); +DEF_DLL_FN (hb_buffer_t *, hb_buffer_create, (void)); +DEF_DLL_FN (void, hb_buffer_set_unicode_funcs, + (hb_buffer_t *, hb_unicode_funcs_t *)); +DEF_DLL_FN (void, hb_buffer_clear_contents, (hb_buffer_t *)); +DEF_DLL_FN (hb_bool_t, hb_buffer_pre_allocate, (hb_buffer_t *, unsigned int)); +DEF_DLL_FN (void, hb_buffer_add, (hb_buffer_t *, hb_codepoint_t, unsigned int)); +DEF_DLL_FN (void, hb_buffer_set_content_type, + (hb_buffer_t *, hb_buffer_content_type_t)); +DEF_DLL_FN (void, hb_buffer_set_cluster_level, + (hb_buffer_t *, hb_buffer_cluster_level_t)); +DEF_DLL_FN (void, hb_buffer_set_direction, (hb_buffer_t *, hb_direction_t)); +DEF_DLL_FN (void, hb_buffer_set_language, (hb_buffer_t *, hb_language_t)); +DEF_DLL_FN (hb_language_t, hb_language_from_string, (const char *, int)); +DEF_DLL_FN (void, hb_buffer_guess_segment_properties, (hb_buffer_t *)); +DEF_DLL_FN (hb_bool_t, hb_shape_full, + (hb_font_t *, hb_buffer_t *, const hb_feature_t *, + unsigned int, const char * const *)); +DEF_DLL_FN (unsigned int, hb_buffer_get_length, (hb_buffer_t *)); +DEF_DLL_FN (hb_direction_t, hb_buffer_get_direction, (hb_buffer_t *)); +DEF_DLL_FN (void, hb_buffer_reverse_clusters, (hb_buffer_t *)); +DEF_DLL_FN (hb_glyph_info_t *, hb_buffer_get_glyph_infos, + (hb_buffer_t *, unsigned int *)); +DEF_DLL_FN (hb_glyph_position_t *, hb_buffer_get_glyph_positions, + (hb_buffer_t *, unsigned int *)); + +#define hb_blob_create fn_hb_blob_create +#define hb_face_create_for_tables fn_hb_face_create_for_tables +#define hb_face_get_glyph_count fn_hb_face_get_glyph_count +#define hb_font_create fn_hb_font_create +#define hb_font_destroy fn_hb_font_destroy +#define hb_face_destroy fn_hb_face_destroy +#define hb_face_get_upem fn_hb_face_get_upem +#define hb_font_get_nominal_glyph fn_hb_font_get_nominal_glyph +#define hb_unicode_funcs_create fn_hb_unicode_funcs_create +#define hb_unicode_funcs_get_default fn_hb_unicode_funcs_get_default +#define hb_unicode_funcs_set_combining_class_func fn_hb_unicode_funcs_set_combining_class_func +#define hb_unicode_funcs_set_general_category_func fn_hb_unicode_funcs_set_general_category_func +#define hb_unicode_funcs_set_mirroring_func fn_hb_unicode_funcs_set_mirroring_func +#define hb_buffer_create fn_hb_buffer_create +#define hb_buffer_set_unicode_funcs fn_hb_buffer_set_unicode_funcs +#define hb_buffer_clear_contents fn_hb_buffer_clear_contents +#define hb_buffer_pre_allocate fn_hb_buffer_pre_allocate +#define hb_buffer_add fn_hb_buffer_add +#define hb_buffer_set_content_type fn_hb_buffer_set_content_type +#define hb_buffer_set_cluster_level fn_hb_buffer_set_cluster_level +#define hb_buffer_set_direction fn_hb_buffer_set_direction +#define hb_buffer_set_language fn_hb_buffer_set_language +#define hb_language_from_string fn_hb_language_from_string +#define hb_buffer_guess_segment_properties fn_hb_buffer_guess_segment_properties +#define hb_shape_full fn_hb_shape_full +#define hb_buffer_get_length fn_hb_buffer_get_length +#define hb_buffer_get_direction fn_hb_buffer_get_direction +#define hb_buffer_reverse_clusters fn_hb_buffer_reverse_clusters +#define hb_buffer_get_glyph_infos fn_hb_buffer_get_glyph_infos +#define hb_buffer_get_glyph_positions fn_hb_buffer_get_glyph_positions +#endif + /* Used by uniscribe_otf_capability. */ static Lisp_Object otf_features (HDC context, const char *table); @@ -117,7 +220,10 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size) struct uniscribe_font_info *uniscribe_font = (struct uniscribe_font_info *) XFONT_OBJECT (font_object); - ASET (font_object, FONT_TYPE_INDEX, Quniscribe); + if (!NILP (AREF (font_entity, FONT_TYPE_INDEX))) + ASET (font_object, FONT_TYPE_INDEX, AREF (font_entity, FONT_TYPE_INDEX)); + else /* paranoia: this should never happen */ + ASET (font_object, FONT_TYPE_INDEX, Quniscribe); if (!w32font_open_internal (f, font_entity, pixel_size, font_object)) { @@ -127,10 +233,15 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size) /* Initialize the cache for this font. */ uniscribe_font->cache = NULL; - /* Uniscribe backend uses glyph indices. */ + /* Uniscribe and HarfBuzz backends use glyph indices. */ uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX; - uniscribe_font->w32_font.font.driver = &uniscribe_font_driver; +#ifdef HAVE_HARFBUZZ + if (EQ (AREF (font_object, FONT_TYPE_INDEX), Qharfbuzz)) + uniscribe_font->w32_font.font.driver = &harfbuzz_font_driver; + else +#endif /* HAVE_HARFBUZZ */ + uniscribe_font->w32_font.font.driver = &uniscribe_font_driver; return font_object; } @@ -141,8 +252,16 @@ uniscribe_close (struct font *font) struct uniscribe_font_info *uniscribe_font = (struct uniscribe_font_info *) font; +#ifdef HAVE_HARFBUZZ + if (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver + && uniscribe_font->cache) + hb_font_destroy ((hb_font_t *) uniscribe_font->cache); + else +#endif if (uniscribe_font->cache) - ScriptFreeCache (&(uniscribe_font->cache)); + ScriptFreeCache ((SCRIPT_CACHE) &(uniscribe_font->cache)); + + uniscribe_font->cache = NULL; w32font_close (font); } @@ -290,7 +409,7 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) /* Context may be NULL here, in which case the cache should be used without needing to select the font. */ - result = ScriptShape (context, &(uniscribe_font->cache), + result = ScriptShape (context, (SCRIPT_CACHE) &(uniscribe_font->cache), chars + items[i].iCharPos, nchars_in_run, max_glyphs - done_glyphs, &(items[i].a), glyphs, clusters, attributes, &nglyphs); @@ -304,7 +423,7 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptShape (context, &(uniscribe_font->cache), + result = ScriptShape (context, (SCRIPT_CACHE) &(uniscribe_font->cache), chars + items[i].iCharPos, nchars_in_run, max_glyphs - done_glyphs, &(items[i].a), glyphs, clusters, attributes, &nglyphs); @@ -329,7 +448,7 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) } else { - result = ScriptPlace (context, &(uniscribe_font->cache), + result = ScriptPlace (context, (SCRIPT_CACHE) &(uniscribe_font->cache), glyphs, nglyphs, attributes, &(items[i].a), advances, offsets, &overall_metrics); if (result == E_PENDING && !context) @@ -339,13 +458,15 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptPlace (context, &(uniscribe_font->cache), + result = ScriptPlace (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), glyphs, nglyphs, attributes, &(items[i].a), advances, offsets, &overall_metrics); } if (SUCCEEDED (result)) { int j, from, to, adj_offset = 0; + int cluster_offset = 0; from = 0; to = from; @@ -389,6 +510,7 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) } } } + cluster_offset = 0; /* For RTL text, the Uniscribe shaper prepares the values in ADVANCES array for layout in @@ -419,8 +541,11 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) } } - LGLYPH_SET_CHAR (lglyph, chars[items[i].iCharPos - + from]); + int char_idx = items[i].iCharPos + from + cluster_offset; + if (from + cluster_offset > to) + char_idx = items[i].iCharPos + to; + cluster_offset++; + LGLYPH_SET_CHAR (lglyph, chars[char_idx]); LGLYPH_SET_FROM (lglyph, items[i].iCharPos + from); LGLYPH_SET_TO (lglyph, items[i].iCharPos + to); @@ -429,18 +554,18 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) LGLYPH_SET_ASCENT (lglyph, font->ascent); LGLYPH_SET_DESCENT (lglyph, font->descent); - result = ScriptGetGlyphABCWidth (context, - &(uniscribe_font->cache), - glyphs[j], &char_metric); + result = ScriptGetGlyphABCWidth + (context, (SCRIPT_CACHE) &(uniscribe_font->cache), + glyphs[j], &char_metric); if (result == E_PENDING && !context) { /* Cache incomplete... */ f = XFRAME (selected_frame); context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptGetGlyphABCWidth (context, - &(uniscribe_font->cache), - glyphs[j], &char_metric); + result = ScriptGetGlyphABCWidth + (context, (SCRIPT_CACHE) &(uniscribe_font->cache), + glyphs[j], &char_metric); } if (SUCCEEDED (result)) @@ -572,7 +697,8 @@ uniscribe_encode_char (struct font *font, int c) order. */ items[0].a.fLogicalOrder = 1; - result = ScriptShape (context, &(uniscribe_font->cache), + result = ScriptShape (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), ch, len, 2, &(items[0].a), glyphs, clusters, attrs, &nglyphs); @@ -583,7 +709,8 @@ uniscribe_encode_char (struct font *font, int c) f = XFRAME (selected_frame); context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptShape (context, &(uniscribe_font->cache), + result = ScriptShape (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), ch, len, 2, &(items[0].a), glyphs, clusters, attrs, &nglyphs); } @@ -601,7 +728,8 @@ uniscribe_encode_char (struct font *font, int c) when shaped. But we still need the return from here to be valid for the shaping engine to be invoked later. */ - result = ScriptGetCMap (context, &(uniscribe_font->cache), + result = ScriptGetCMap (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), ch, len, 0, glyphs); if (SUCCEEDED (result) && glyphs[0]) code = glyphs[0]; @@ -1148,6 +1276,508 @@ font_table_error: return Qnil; } +#ifdef HAVE_HARFBUZZ + +/* W32 implementation of the 'list' method for HarfBuzz backend. */ +static Lisp_Object +w32hb_list (struct frame *f, Lisp_Object font_spec) +{ + Lisp_Object fonts = w32font_list_internal (f, font_spec, true); + FONT_ADD_LOG ("harfbuzz-list", font_spec, fonts); + + for (Lisp_Object tail = fonts; CONSP (tail); tail = XCDR (tail)) + ASET (XCAR (tail), FONT_TYPE_INDEX, Qharfbuzz); + + return fonts; +} + +/* W32 implementation of the 'match' method for HarfBuzz backend. */ +static Lisp_Object +w32hb_match (struct frame *f, Lisp_Object font_spec) +{ + Lisp_Object entity = w32font_match_internal (f, font_spec, true); + FONT_ADD_LOG ("harfbuzz-match", font_spec, entity); + + if (! NILP (entity)) + ASET (entity, FONT_TYPE_INDEX, Qharfbuzz); + return entity; +} + +/* Callback function to free memory. We need this so we could pass it + to HarfBuzz as the function to call to destroy objects for which we + allocated data by calling our 'malloc' (as opposed to 'malloc' from + the MS CRT, against which HarfBuzz was linked). */ +static void +free_cb (void *ptr) +{ + free (ptr); +} + +/* A function used as reference_table_func for HarfBuzz. It returns + the data of a specified table of a font as a blob. */ +static hb_blob_t * +w32hb_get_font_table (hb_face_t *face, hb_tag_t tag, void *data) +{ + struct frame *f = XFRAME (selected_frame); + HDC context = get_frame_dc (f); + HFONT old_font = SelectObject (context, (HFONT) data); + char *font_data = NULL; + DWORD font_data_size = 0, val; + DWORD table = bswap_32 (tag); + hb_blob_t *blob = NULL; + + val = GetFontData (context, table, 0, font_data, font_data_size); + if (val != GDI_ERROR) + { + font_data_size = val; + /* Don't call xmalloc, because it can signal an error, while + we are inside a critical section established by get_frame_dc. */ + font_data = malloc (font_data_size); + if (font_data) + { + val = GetFontData (context, table, 0, font_data, font_data_size); + if (val != GDI_ERROR) + blob = hb_blob_create (font_data, font_data_size, + HB_MEMORY_MODE_READONLY, font_data, free_cb); + } + } + + /* Restore graphics context. */ + SelectObject (context, old_font); + release_frame_dc (f, context); + + return blob; +} + +/* Helper function used by the HarfBuzz implementations of the + encode_char, has_char, and begin_hb_font methods. It creates an + hb_font_t object for a given Emacs font. */ +static hb_font_t * +w32hb_get_font (struct font *font, double *scale) +{ + hb_font_t *hb_font = NULL; + HFONT font_handle = FONT_HANDLE (font); + hb_face_t *hb_face = + hb_face_create_for_tables (w32hb_get_font_table, font_handle, NULL); + if (hb_face_get_glyph_count (hb_face) > 0) + hb_font = hb_font_create (hb_face); + + struct uniscribe_font_info *uniscribe_font = + (struct uniscribe_font_info *) font; + unsigned upem = hb_face_get_upem (hb_face); + eassert (upem > 0); + /* https://support.microsoft.com/en-sg/help/74299/info-calculating-the-logical-height-and-point-size-of-a-font. */ + LONG font_point_size = + uniscribe_font->w32_font.metrics.tmHeight + - uniscribe_font->w32_font.metrics.tmInternalLeading; + /* https://docs.microsoft.com/en-us/typography/opentype/spec/ttch01, + under "Converting FUnits to pixels". */ + *scale = font_point_size * 1.0 / upem; + + hb_face_destroy (hb_face); + + /* FIXME: Can hb_font be non-NULL and yet invalid? Compare to get_empty? */ + return hb_font; +} + +/* W32 implementation of encode_char method for HarfBuzz backend. */ +static unsigned +w32hb_encode_char (struct font *font, int c) +{ + struct uniscribe_font_info *uniscribe_font + = (struct uniscribe_font_info *) font; + eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver); + hb_font_t *hb_font = uniscribe_font->cache; + + /* First time we use this font with HarfBuzz, create the hb_font_t + object and cache it. */ + if (!hb_font) + { + double scale; + hb_font = w32hb_get_font (font, &scale); + if (!hb_font) + return FONT_INVALID_CODE; + + uniscribe_font->cache = hb_font; + eassert (scale > 0.0); + uniscribe_font->scale = scale; + } + hb_codepoint_t glyph; + if (hb_font_get_nominal_glyph (hb_font, c, &glyph)) + return glyph; + return FONT_INVALID_CODE; +} + +/* W32 implementation of HarfBuzz begin_hb_font and end_hb_font + methods. */ + +/* Return a HarfBuzz font object for FONT and store in POSITION_UNIT + the scale factor to convert a hb_position_t value to the number of + pixels. Return NULL if HarfBuzz font object is not available for + FONT. */ +static hb_font_t * +w32hb_begin_font (struct font *font, double *position_unit) +{ + struct uniscribe_font_info *uniscribe_font + = (struct uniscribe_font_info *) font; + eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver); + + /* First time we use this font with HarfBuzz, create the hb_font_t + object and cache it. */ + if (!uniscribe_font->cache) + { + double scale; + uniscribe_font->cache = w32hb_get_font (font, &scale); + eassert (scale > 0.0); + uniscribe_font->scale = scale; + } + *position_unit = uniscribe_font->scale; + return (hb_font_t *) uniscribe_font->cache; +} + +static bool combining_class_loaded = false; +static Lisp_Object canonical_combining_class_table; + +static hb_unicode_combining_class_t +w32uni_combining (hb_unicode_funcs_t *funcs, hb_codepoint_t ch, void *user_data) +{ + /* Load the Unicode table first time it is needed. */ + if (!combining_class_loaded) + { + canonical_combining_class_table = + uniprop_table (intern ("canonical-combining-class")); + if (NILP (canonical_combining_class_table)) + emacs_abort (); + staticpro (&canonical_combining_class_table); + combining_class_loaded = true; + } + + Lisp_Object combining = + get_unicode_property (canonical_combining_class_table, ch); + if (FIXNUMP (combining)) + return (hb_unicode_combining_class_t) XFIXNUM (combining); + + return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED; +} + +static hb_unicode_general_category_t +w32uni_general (hb_unicode_funcs_t *funcs, hb_codepoint_t ch, void *user_data) +{ + Lisp_Object category = CHAR_TABLE_REF (Vunicode_category_table, ch); + + if (INTEGERP (category)) + { + switch (XFIXNUM (category)) + { + case UNICODE_CATEGORY_Cc: + return HB_UNICODE_GENERAL_CATEGORY_CONTROL; + case UNICODE_CATEGORY_Cf: + return HB_UNICODE_GENERAL_CATEGORY_FORMAT; + case UNICODE_CATEGORY_Cn: + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; + case UNICODE_CATEGORY_Co: + return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE; + case UNICODE_CATEGORY_Cs: + return HB_UNICODE_GENERAL_CATEGORY_SURROGATE; + case UNICODE_CATEGORY_Ll: + return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER; + case UNICODE_CATEGORY_Lm: + return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER; + case UNICODE_CATEGORY_Lo: + return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; + case UNICODE_CATEGORY_Lt: + return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER; + case UNICODE_CATEGORY_Lu: + return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER; + case UNICODE_CATEGORY_Mc: + return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK; + case UNICODE_CATEGORY_Me: + return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK; + case UNICODE_CATEGORY_Mn: + return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK; + case UNICODE_CATEGORY_Nd: + return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER; + case UNICODE_CATEGORY_Nl: + return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER; + case UNICODE_CATEGORY_No: + return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER; + case UNICODE_CATEGORY_Pc: + return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION; + case UNICODE_CATEGORY_Pd: + return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION; + case UNICODE_CATEGORY_Pe: + return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION; + case UNICODE_CATEGORY_Pf: + return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION; + case UNICODE_CATEGORY_Pi: + return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION; + case UNICODE_CATEGORY_Po: + return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION; + case UNICODE_CATEGORY_Ps: + return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION; + case UNICODE_CATEGORY_Sc: + return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL; + case UNICODE_CATEGORY_Sk: + return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL; + case UNICODE_CATEGORY_Sm: + return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL; + case UNICODE_CATEGORY_So: + return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL; + case UNICODE_CATEGORY_Zl: + return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR; + case UNICODE_CATEGORY_Zp: + return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR; + case UNICODE_CATEGORY_Zs: + return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; + case UNICODE_CATEGORY_UNKNOWN: + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; + } + } + + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; +} + +static hb_codepoint_t +w32uni_mirroring (hb_unicode_funcs_t *funcs, hb_codepoint_t ch, void *user_data) +{ + return bidi_mirror_char (ch); +} + +static hb_unicode_funcs_t * +get_hb_unicode_funcs (void) +{ + /* Subclass HarfBuzz's default Unicode functions and override functions that + * use data Emacs can provide. This way changing Emacs data is reflected in + * the shaped output. */ + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (hb_unicode_funcs_get_default ()); + + hb_unicode_funcs_set_combining_class_func (funcs, w32uni_combining, NULL, NULL); + hb_unicode_funcs_set_general_category_func (funcs, w32uni_general, NULL, NULL); + hb_unicode_funcs_set_mirroring_func (funcs, w32uni_mirroring, NULL, NULL); + + /* Use default implmentation for Unicode composition/decomposition, we might + * want to revisit this later. + hb_unicode_funcs_set_compose_func (funcs, uni_compose, NULL, NULL); + hb_unicode_funcs_set_decompose_func (funcs, uni_decompose, NULL, NULL); + */ + + /* Emacs own script mapping for characters differs from Unicode, so we want + * to keep the default HarfBuzz's implementation here. + hb_unicode_funcs_set_script_func (funcs, uni_script, NULL, NULL); + */ + + return funcs; +} + +/* HarfBuzz implementation of shape for font backend. See the + commentary before uniscribe_shape for the meaning of the + arguments. */ +static Lisp_Object +w32hb_shape (Lisp_Object lgstring, Lisp_Object direction) +{ + struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring)); + ptrdiff_t glyph_len = 0, text_len = LGSTRING_GLYPH_LEN (lgstring); + ptrdiff_t i; + + hb_glyph_info_t *info; + hb_glyph_position_t *pos; + + /* Cache the HarfBuzz buffer for better performance and less allocations. + * We intentionally never destroy the buffer. */ + static hb_buffer_t *hb_buffer = NULL; + if (! hb_buffer) + { + hb_buffer = hb_buffer_create (); + hb_unicode_funcs_t* ufuncs = get_hb_unicode_funcs(); + hb_buffer_set_unicode_funcs(hb_buffer, ufuncs); + } + + hb_buffer_clear_contents (hb_buffer); + hb_buffer_pre_allocate (hb_buffer, text_len); + + /* Copy the characters in their original logical order, so we can + assign them to glyphs correctly after shaping. */ + int *chars = alloca (text_len * sizeof (int)); + for (i = 0; i < text_len; i++) + { + Lisp_Object g = LGSTRING_GLYPH (lgstring, i); + int c; + + if (NILP (g)) + break; + c = LGLYPH_CHAR (g); + hb_buffer_add (hb_buffer, c, i); + chars[i] = c; + } + + text_len = i; + if (!text_len) + return Qnil; + + hb_buffer_set_content_type (hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + hb_buffer_set_cluster_level (hb_buffer, + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES); + + /* If the caller didn't provide a meaningful DIRECTION, let HarfBuzz + guess it. */ + if (!NILP (direction)) + { + hb_direction_t dir = HB_DIRECTION_LTR; + if (EQ (direction, QL2R)) + dir = HB_DIRECTION_LTR; + else if (EQ (direction, QR2L)) + dir = HB_DIRECTION_RTL; + hb_buffer_set_direction (hb_buffer, dir); + } + + /* Leave the script determination to HarfBuzz, until Emacs has a + better idea of the script of LGSTRING. FIXME. */ +#if 0 + hb_buffer_set_script (hb_buffer, XXX); +#endif + + /* FIXME: This can only handle the single global language, which + normally comes from the locale. In addition, if + current-iso639-language is a list, we arbitrarily use the first + one. We should instead have a notion of the language of the text + being shaped. */ + Lisp_Object lang = Vcurrent_iso639_language; + if (CONSP (Vcurrent_iso639_language)) + lang = XCAR (Vcurrent_iso639_language); + if (SYMBOLP (lang)) + { + Lisp_Object lang_str = SYMBOL_NAME (lang); + hb_buffer_set_language (hb_buffer, + hb_language_from_string (SSDATA (lang_str), + SBYTES (lang_str))); + } + + /* Guess the default properties for when they cannot be determined above. + + FIXME: maybe drop this guessing once script and language handling + is fixed above; but then will need to guess the direction by + ourselves, perhaps by looking at the the characters using + bidi_get_type or somesuch. */ + hb_buffer_guess_segment_properties (hb_buffer); + + double position_unit; + hb_font_t *hb_font + = font->driver->begin_hb_font + ? font->driver->begin_hb_font (font, &position_unit) + : NULL; + if (!hb_font) + return make_fixnum (0); + + hb_bool_t success = hb_shape_full (hb_font, hb_buffer, NULL, 0, NULL); + if (font->driver->end_hb_font) + font->driver->end_hb_font (font, hb_font); + if (!success) + return Qnil; + + glyph_len = hb_buffer_get_length (hb_buffer); + /* FIXME: can't we just grow the lgstring in this case? Giving up is an + * overly heavy handed solution. */ + if (glyph_len > LGSTRING_GLYPH_LEN (lgstring)) + return Qnil; + + /* We need the clusters in logical order. */ + bool buf_reversed = false; + if (HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (hb_buffer))) + { + buf_reversed = true; + hb_buffer_reverse_clusters (hb_buffer); + } + info = hb_buffer_get_glyph_infos (hb_buffer, NULL); + pos = hb_buffer_get_glyph_positions (hb_buffer, NULL); + int from = -1, to, cluster_offset = 0; + int char_idx, incr = buf_reversed ? -1 : 1; + for (i = 0; i < glyph_len; i++) + { + Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i); + struct font_metrics metrics = {.width = 0}; + int xoff, yoff, wadjust; + + if (NILP (lglyph)) + { + lglyph = LGLYPH_NEW (); + LGSTRING_SET_GLYPH (lgstring, i, lglyph); + } + + if (info[i].cluster != from) + { + int j; + /* Found a new cluster. Determine its FROM and TO, and the + offset to the first character of the cluster. */ + /* FROM is the index of the first character that contributed + to this cluster. */ + from = info[i].cluster; + /* TO is the index of the last character that contributed to + this cluster. */ + for (j = i; j < glyph_len && info[j].cluster == from; j++) + ; + to = (j == glyph_len) ? text_len - 1 : info[j].cluster - 1; + cluster_offset = 0; + /* For RTL buffers, HarfBuzz produces glyphs in a cluster in + reverse order, so we need to account for that to record + the correct character in each glyph. + + Implementation note: the character codepoint recorded in + each glyph is not really used, except when we display the + glyphs in descr-text.el. So this is just an aeasthetic + issue. */ + if (buf_reversed) + cluster_offset = to - from; + } + + /* All the glyphs in a cluster have the same values of FROM and TO. */ + LGLYPH_SET_FROM (lglyph, from); + LGLYPH_SET_TO (lglyph, to); + + /* Not every glyph in a cluster maps directly to a single + character; in general, N characters can yield M glyphs, where + M could be smaller or greater than N. However, in many cases + there is a one-to-one correspondence, and it would be a pity + to lose that information, even if it's sometimes inaccurate. */ + char_idx = from + cluster_offset; + cluster_offset += incr; + if (char_idx > to) + char_idx = to; + if (char_idx < from) + char_idx = from; + LGLYPH_SET_CHAR (lglyph, chars[char_idx]); + LGLYPH_SET_CODE (lglyph, info[i].codepoint); + + unsigned code = info[i].codepoint; + font->driver->text_extents (font, &code, 1, &metrics); + LGLYPH_SET_WIDTH (lglyph, metrics.width); + LGLYPH_SET_LBEARING (lglyph, metrics.lbearing); + LGLYPH_SET_RBEARING (lglyph, metrics.rbearing); + LGLYPH_SET_ASCENT (lglyph, metrics.ascent); + LGLYPH_SET_DESCENT (lglyph, metrics.descent); + + xoff = lround (pos[i].x_offset * position_unit); + yoff = - lround (pos[i].y_offset * position_unit); + wadjust = lround (pos[i].x_advance * position_unit); + if (xoff || yoff || wadjust != metrics.width) + { + Lisp_Object vec = make_uninit_vector (3); + ASET (vec, 0, make_fixnum (xoff)); + ASET (vec, 1, make_fixnum (yoff)); + ASET (vec, 2, make_fixnum (wadjust)); + LGLYPH_SET_ADJUSTMENT (lglyph, vec); + } + } + + return make_fixnum (glyph_len); +} + +static Lisp_Object +w32hb_combining_capability (struct font *font) +{ + return Qt; +} +#endif /* HAVE_HARFBUZZ */ + #undef OTF_INT16_VAL #undef OTF_TAG_VAL #undef OTF_TAG @@ -1196,17 +1826,53 @@ syms_of_w32uniscribe (void) pdumper_do_now_and_after_load (syms_of_w32uniscribe_for_pdumper); } +#ifdef HAVE_HARFBUZZ +static bool +load_harfbuzz_funcs (HMODULE library) +{ + LOAD_DLL_FN (library, hb_blob_create); + LOAD_DLL_FN (library, hb_face_create_for_tables); + LOAD_DLL_FN (library, hb_face_get_glyph_count); + LOAD_DLL_FN (library, hb_font_create); + LOAD_DLL_FN (library, hb_font_destroy); + LOAD_DLL_FN (library, hb_face_get_upem); + LOAD_DLL_FN (library, hb_face_destroy); + LOAD_DLL_FN (library, hb_font_get_nominal_glyph); + LOAD_DLL_FN (library, hb_unicode_funcs_create); + LOAD_DLL_FN (library, hb_unicode_funcs_get_default); + LOAD_DLL_FN (library, hb_unicode_funcs_set_combining_class_func); + LOAD_DLL_FN (library, hb_unicode_funcs_set_general_category_func); + LOAD_DLL_FN (library, hb_unicode_funcs_set_mirroring_func); + LOAD_DLL_FN (library, hb_buffer_create); + LOAD_DLL_FN (library, hb_buffer_set_unicode_funcs); + LOAD_DLL_FN (library, hb_buffer_clear_contents); + LOAD_DLL_FN (library, hb_buffer_pre_allocate); + LOAD_DLL_FN (library, hb_buffer_add); + LOAD_DLL_FN (library, hb_buffer_set_content_type); + LOAD_DLL_FN (library, hb_buffer_set_cluster_level); + LOAD_DLL_FN (library, hb_buffer_set_direction); + LOAD_DLL_FN (library, hb_buffer_set_language); + LOAD_DLL_FN (library, hb_language_from_string); + LOAD_DLL_FN (library, hb_buffer_guess_segment_properties); + LOAD_DLL_FN (library, hb_shape_full); + LOAD_DLL_FN (library, hb_buffer_get_length); + LOAD_DLL_FN (library, hb_buffer_get_direction); + LOAD_DLL_FN (library, hb_buffer_reverse_clusters); + LOAD_DLL_FN (library, hb_buffer_get_glyph_infos); + LOAD_DLL_FN (library, hb_buffer_get_glyph_positions); + return true; +} +#endif /* HAVE_HARFBUZZ */ + static void syms_of_w32uniscribe_for_pdumper (void) { - HMODULE uniscribe; - - /* Don't init uniscribe when dumping */ + /* Don't init Uniscribe and HarfBuzz when dumping */ if (!initialized) return; - /* Don't register if uniscribe is not available. */ - uniscribe = GetModuleHandle ("usp10"); + /* Don't register if Uniscribe is not available. */ + HMODULE uniscribe = GetModuleHandle ("usp10"); if (!uniscribe) return; @@ -1226,4 +1892,30 @@ syms_of_w32uniscribe_for_pdumper (void) uniscribe_new_apis = true; else uniscribe_new_apis = false; + +#ifdef HAVE_HARFBUZZ + /* Currently, HarfBuzz DLLs are always named libharfbuzz-0.dll, as + the project keeps the ABI backeard-compatible. So we can + hard-code the name of the library here, for now. If they ever + break ABI compatibility, we may need to load the DLL that + corresponds to the HarfBuzz version for which Emacs was built. */ + HMODULE harfbuzz = LoadLibrary ("libharfbuzz-0.dll"); + /* Don't register if HarfBuzz is not available. */ + if (!harfbuzz) + return; + + if (!load_harfbuzz_funcs (harfbuzz)) + return; + + harfbuzz_available = 1; + harfbuzz_font_driver = uniscribe_font_driver; + harfbuzz_font_driver.type = Qharfbuzz; + harfbuzz_font_driver.list = w32hb_list; + harfbuzz_font_driver.match = w32hb_match; + harfbuzz_font_driver.encode_char = w32hb_encode_char; + harfbuzz_font_driver.shape = w32hb_shape; + harfbuzz_font_driver.combining_capability = w32hb_combining_capability; + harfbuzz_font_driver.begin_hb_font = w32hb_begin_font; + register_font_driver (&harfbuzz_font_driver, NULL); +#endif /* HAVE_HARFBUZZ */ } -- 2.39.2