diff options
author | Eli Zaretskii <eliz@gnu.org> | 2019-06-01 12:53:41 +0300 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2019-06-01 12:53:41 +0300 |
commit | 4363777d5c60af8bc93b30d4f6c5e12dc2761160 (patch) | |
tree | 2ff9ea97c469140df5630f6ee5c250e745a78dcb /src/hbfont.c | |
parent | b7730c259bc91f030069e408aaddb43f02da3a3a (diff) | |
download | emacs-4363777d5c60af8bc93b30d4f6c5e12dc2761160.tar.gz emacs-4363777d5c60af8bc93b30d4f6c5e12dc2761160.tar.bz2 emacs-4363777d5c60af8bc93b30d4f6c5e12dc2761160.zip |
Move common HarfBuzz code to a common file hbfont.c
* src/hbfont.c: New file, with code moved from w32uniscribe.c
and renamed/modified as appropriate.
* src/w32uniscribe.c: Move to hbfont.c DEF_DLL_FN and macro
definitions for HarfBuzz functions used in hbfont.c
(load_harfbuzz_funcs): Move loading of HarfBuzz functions used
by hbfont.c to hbfont.c:hbfont_init_w32_funcs, and call that
function from here.
(syms_of_w32uniscribe_for_pdumper): Fill the 'shape' and
'combining_capability' members with hbfont.c function names.
* src/w32common.h (hbfont_init_w32_funcs) [HAVE_HARFBUZZ]: Add
prototype.
* src/font.h (hbfont_shape, hbfont_combining_capability)
[HAVE_HARFBUZZ]: Add prototypes.
* src/Makefile.in (SOME_MACHINE_OBJECTS): Add hbfont.o.
* configure.ac (FONT_OBJ): Add hbfont.o if HAVE_HARFBUZZ.
Diffstat (limited to 'src/hbfont.c')
-rw-r--r-- | src/hbfont.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/hbfont.c b/src/hbfont.c new file mode 100644 index 00000000000..392414e9109 --- /dev/null +++ b/src/hbfont.c @@ -0,0 +1,484 @@ +/* hbfont.c -- Platform-independent support for HarfBuzz font driver. + Copyright (C) 2019 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 "lisp.h" +#include "frame.h" +#include "composite.h" +#include "font.h" +#include "dispextern.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 *)); + +#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 + +/* 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); + return true; +} +#endif /* HAVE_NTGUI */ + +/* 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 (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 +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 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. + + 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 fuinction. + 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)) + { + 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); + 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); +} + +Lisp_Object +hbfont_combining_capability (struct font *font) +{ + return Qt; +} |