summaryrefslogtreecommitdiff
path: root/src/hbfont.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hbfont.c')
-rw-r--r--src/hbfont.c610
1 files changed, 610 insertions, 0 deletions
diff --git a/src/hbfont.c b/src/hbfont.c
new file mode 100644
index 00000000000..476e08020e1
--- /dev/null
+++ b/src/hbfont.c
@@ -0,0 +1,610 @@
+/* hbfont.c -- Platform-independent support for HarfBuzz font driver.
+ Copyright (C) 2019-2022 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <math.h>
+#include <hb.h>
+#include <hb-ot.h>
+
+#include "lisp.h"
+#include "frame.h"
+#include "composite.h"
+#include "font.h"
+#include "dispextern.h"
+#include "buffer.h"
+
+#ifdef HAVE_NTGUI
+
+#include "w32common.h"
+
+/* The w32 implementation calls HarfBuzz functions via function
+ pointers. We use the below to declare the function pointers and
+ redirect function names to those pointers. */
+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 *));
+DEF_DLL_FN (void, hb_tag_to_string, (hb_tag_t, char *));
+DEF_DLL_FN (hb_face_t *, hb_font_get_face, (hb_font_t *font));
+DEF_DLL_FN (unsigned int, hb_ot_layout_table_get_script_tags,
+ (hb_face_t *, hb_tag_t, unsigned int, unsigned int *, hb_tag_t *));
+DEF_DLL_FN (unsigned int, hb_ot_layout_table_get_feature_tags,
+ (hb_face_t *, hb_tag_t, unsigned int, unsigned int *, hb_tag_t *));
+DEF_DLL_FN (unsigned int, hb_ot_layout_script_get_language_tags,
+ (hb_face_t *, hb_tag_t, unsigned int, unsigned int, unsigned int *,
+ hb_tag_t *));
+DEF_DLL_FN (unsigned int, hb_ot_layout_language_get_feature_tags,
+ (hb_face_t *, hb_tag_t, unsigned int, unsigned int, unsigned int,
+ unsigned int *, hb_tag_t *));
+
+#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
+#define hb_tag_to_string fn_hb_tag_to_string
+#define hb_font_get_face fn_hb_font_get_face
+#define hb_ot_layout_table_get_script_tags fn_hb_ot_layout_table_get_script_tags
+#define hb_ot_layout_table_get_feature_tags fn_hb_ot_layout_table_get_feature_tags
+#define hb_ot_layout_script_get_language_tags fn_hb_ot_layout_script_get_language_tags
+#define hb_ot_layout_language_get_feature_tags fn_hb_ot_layout_language_get_feature_tags
+
+/* This function is called from syms_of_w32uniscribe_for_pdumper to
+ initialize the above function pointers. */
+bool
+hbfont_init_w32_funcs (HMODULE library)
+{
+ 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);
+ LOAD_DLL_FN (library, hb_tag_to_string);
+ LOAD_DLL_FN (library, hb_font_get_face);
+ LOAD_DLL_FN (library, hb_ot_layout_table_get_script_tags);
+ LOAD_DLL_FN (library, hb_ot_layout_table_get_feature_tags);
+ LOAD_DLL_FN (library, hb_ot_layout_script_get_language_tags);
+ LOAD_DLL_FN (library, hb_ot_layout_language_get_feature_tags);
+ return true;
+}
+#endif /* HAVE_NTGUI */
+
+static Lisp_Object
+hbfont_otf_features (hb_face_t *face, hb_tag_t table_tag)
+{
+ hb_tag_t *language_tags = NULL, *feature_tags = NULL;
+ char buf[4];
+ unsigned int script_count
+ = hb_ot_layout_table_get_script_tags (face, table_tag, 0, NULL, NULL);
+ hb_tag_t *script_tags = xnmalloc (script_count, sizeof *script_tags);
+ hb_ot_layout_table_get_script_tags (face, table_tag, 0, &script_count,
+ script_tags);
+ Lisp_Object scripts = Qnil;
+ for (int i = script_count - 1; i >= 0; i--)
+ {
+ unsigned int language_count
+ = hb_ot_layout_script_get_language_tags (face, table_tag, i, 0,
+ NULL, NULL);
+ language_tags = xnrealloc (language_tags, language_count,
+ sizeof *language_tags);
+ hb_ot_layout_script_get_language_tags (face, table_tag, i, 0,
+ &language_count, language_tags);
+ Lisp_Object langsyses = Qnil;
+ for (int j = language_count - 1; j >= -1; j--)
+ {
+ unsigned int language_index
+ = j >= 0 ? j : HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
+ unsigned int feature_count
+ = hb_ot_layout_language_get_feature_tags (face, table_tag,
+ i, language_index, 0,
+ NULL, NULL);
+ if (feature_count == 0)
+ continue;
+ feature_tags = xnrealloc (feature_tags, feature_count,
+ sizeof *feature_tags);
+ hb_ot_layout_language_get_feature_tags (face, table_tag,
+ i, language_index, 0,
+ &feature_count, feature_tags);
+ Lisp_Object features = Qnil;
+ for (int k = feature_count - 1; k >= 0; k--)
+ {
+ hb_tag_to_string (feature_tags[k], buf);
+ features = Fcons (font_intern_prop (buf, 4, 1), features);
+ }
+
+ Lisp_Object sym = Qnil;
+ if (j >= 0)
+ {
+ hb_tag_to_string (language_tags[j], buf);
+ sym = font_intern_prop (buf, 4, 1);
+ }
+ langsyses = Fcons (Fcons (sym, features), langsyses);
+ }
+
+ hb_tag_to_string (script_tags[i], buf);
+ scripts = Fcons (Fcons (font_intern_prop (buf, 4, 1), langsyses),
+ scripts);
+ }
+ xfree (feature_tags);
+ xfree (language_tags);
+ xfree (script_tags);
+
+ return scripts;
+}
+
+Lisp_Object
+hbfont_otf_capability (struct font *font)
+{
+ 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 Qnil;
+
+ Lisp_Object gsub_gpos = Fcons (Qnil, Qnil);
+ hb_face_t *face = hb_font_get_face (hb_font);
+ if (hb_ot_layout_table_get_feature_tags (face, HB_OT_TAG_GSUB, 0, NULL, NULL))
+ XSETCAR (gsub_gpos, hbfont_otf_features (face, HB_OT_TAG_GSUB));
+ if (hb_ot_layout_table_get_feature_tags (face, HB_OT_TAG_GPOS, 0, NULL, NULL))
+ XSETCDR (gsub_gpos, hbfont_otf_features (face, HB_OT_TAG_GPOS));
+
+ if (font->driver->end_hb_font)
+ font->driver->end_hb_font (font, hb_font);
+
+ return gsub_gpos;
+}
+
+/* Support functions for HarfBuzz shaper. */
+
+static bool combining_class_loaded = false;
+static Lisp_Object canonical_combining_class_table;
+
+static hb_unicode_combining_class_t
+uni_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 (Qcanonical_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
+uni_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
+uni_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, uni_combining, NULL, NULL);
+ hb_unicode_funcs_set_general_category_func (funcs, uni_general, NULL, NULL);
+ hb_unicode_funcs_set_mirroring_func (funcs, uni_mirroring, NULL, NULL);
+
+ /* Use default implementation 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.
+
+ Shape text in LGSTRING. See the docstring of
+ 'composition-get-gstring' for the format of LGSTRING. If the
+ (N+1)th element of LGSTRING is nil, input of shaping is from the
+ 1st to (N)th elements. In each input glyph, FROM, TO, CHAR, and
+ CODE are already set, but FROM and TO need adjustments according
+ to the glyphs produced by the shaping function.
+ DIRECTION is either L2R or R2L, or nil if unknown. During
+ redisplay, this comes from applying the UBA, is passed from
+ composition_reseat_it, and is used by the HarfBuzz shaper.
+
+ This function updates all fields of the input glyphs. If the
+ output glyphs (M) are more than the input glyphs (N), (N+1)th
+ through (M)th elements of LGSTRING are updated possibly by making
+ a new glyph object and storing it in LGSTRING. If (M) is greater
+ than the length of LGSTRING, nil should be returned. In that case,
+ this function is called again with a larger LGSTRING. */
+Lisp_Object
+hbfont_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)
+ /* If they bind bidi-display-reordering to nil, the DIRECTION
+ they provide is meaningless, and we should let HarfBuzz guess
+ the real direction. */
+ && !NILP (BVAR (current_buffer, bidi_display_reordering)))
+ {
+ 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 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);
+ 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);
+ ptrdiff_t from = -1, to UNINIT, cluster_offset UNINIT;
+ int 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;
+ bool new_lglyph = false;
+
+ if (NILP (lglyph))
+ {
+ new_lglyph = true;
+ 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 aesthetic
+ 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);
+ /* This heuristic is for when the Lisp shape-gstring function
+ substitutes known precomposed characters for decomposed
+ sequences. E.g., hebrew.el does that. This makes TEXT_LEN
+ be smaller than the original length of the composed character
+ sequence. In that case, we must not alter the largest TO,
+ because the display engine must know that all the characters
+ in the original sequence were processed by the composition.
+ If we don't do this, some of the composed characters will be
+ displayed again as separate glyphs. */
+ if (!(!new_lglyph
+ && to == text_len - 1
+ && LGLYPH_TO (lglyph) > to))
+ 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. */
+ ptrdiff_t 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)
+ LGLYPH_SET_ADJUSTMENT (lglyph, CALLN (Fvector,
+ make_fixnum (xoff),
+ make_fixnum (yoff),
+ make_fixnum (wadjust)));
+ }
+
+ return make_fixnum (glyph_len);
+}
+
+Lisp_Object
+hbfont_combining_capability (struct font *font)
+{
+ return Qt;
+}