/* ftcrfont.c -- FreeType font driver on cairo. Copyright (C) 2015-2020 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 . */ #include #include #include #include "lisp.h" #include "xterm.h" #include "blockinput.h" #include "charset.h" #include "composite.h" #include "font.h" #include "ftfont.h" #include "pdumper.h" #define METRICS_NCOLS_PER_ROW (128) enum metrics_status { METRICS_INVALID = -1, /* metrics entry is invalid */ }; #define METRICS_STATUS(metrics) ((metrics)->ascent + (metrics)->descent) #define METRICS_SET_STATUS(metrics, status) \ ((metrics)->ascent = 0, (metrics)->descent = (status)) static int ftcrfont_glyph_extents (struct font *font, unsigned glyph, struct font_metrics *metrics) { struct font_info *ftcrfont_info = (struct font_info *) font; int row, col; struct font_metrics *cache; row = glyph / METRICS_NCOLS_PER_ROW; col = glyph % METRICS_NCOLS_PER_ROW; if (row >= ftcrfont_info->metrics_nrows) { ftcrfont_info->metrics = xrealloc (ftcrfont_info->metrics, sizeof (struct font_metrics *) * (row + 1)); memset (ftcrfont_info->metrics + ftcrfont_info->metrics_nrows, 0, (sizeof (struct font_metrics *) * (row + 1 - ftcrfont_info->metrics_nrows))); ftcrfont_info->metrics_nrows = row + 1; } if (ftcrfont_info->metrics[row] == NULL) { struct font_metrics *new; int i; new = xmalloc (sizeof (struct font_metrics) * METRICS_NCOLS_PER_ROW); for (i = 0; i < METRICS_NCOLS_PER_ROW; i++) METRICS_SET_STATUS (new + i, METRICS_INVALID); ftcrfont_info->metrics[row] = new; } cache = ftcrfont_info->metrics[row] + col; if (METRICS_STATUS (cache) == METRICS_INVALID) { cairo_glyph_t cr_glyph = {.index = glyph}; cairo_text_extents_t extents; cairo_scaled_font_glyph_extents (ftcrfont_info->cr_scaled_font, &cr_glyph, 1, &extents); cache->lbearing = floor (extents.x_bearing); cache->rbearing = ceil (extents.width + extents.x_bearing); cache->width = lround (extents.x_advance); cache->ascent = ceil (- extents.y_bearing); cache->descent = ceil (extents.height + extents.y_bearing); } if (metrics) *metrics = *cache; return cache->width; } static Lisp_Object ftcrfont_list (struct frame *f, Lisp_Object spec) { return ftfont_list2 (f, spec, Qftcr); } static Lisp_Object ftcrfont_match (struct frame *f, Lisp_Object spec) { return ftfont_match2 (f, spec, Qftcr); } static Lisp_Object ftcrfont_open (struct frame *f, Lisp_Object entity, int pixel_size) { FcResult result; Lisp_Object val, filename, font_object; FcPattern *pat, *match; struct font_info *ftcrfont_info; struct font *font; double size = 0; cairo_font_face_t *font_face; cairo_font_extents_t extents; FT_Face ft_face; FcMatrix *matrix; val = assq_no_quit (QCfont_entity, AREF (entity, FONT_EXTRA_INDEX)); if (! CONSP (val)) return Qnil; val = XCDR (val); filename = XCAR (val); size = XFIXNUM (AREF (entity, FONT_SIZE_INDEX)); if (size == 0) size = pixel_size; block_input (); pat = ftfont_entity_pattern (entity, pixel_size); FcConfigSubstitute (NULL, pat, FcMatchPattern); FcDefaultSubstitute (pat); match = FcFontMatch (NULL, pat, &result); ftfont_fix_match (pat, match); FcPatternDestroy (pat); font_face = cairo_ft_font_face_create_for_pattern (match); if (!font_face) { unblock_input (); FcPatternDestroy (match); return Qnil; } cairo_matrix_t font_matrix, ctm; cairo_matrix_init_scale (&font_matrix, pixel_size, pixel_size); cairo_matrix_init_identity (&ctm); cairo_font_options_t *options = cairo_font_options_create (); cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (font_face, &font_matrix, &ctm, options); cairo_font_face_destroy (font_face); cairo_font_options_destroy (options); unblock_input (); font_object = font_build_object (VECSIZE (struct font_info), AREF (entity, FONT_TYPE_INDEX), entity, size); ASET (font_object, FONT_FILE_INDEX, filename); font = XFONT_OBJECT (font_object); font->pixel_size = size; #ifdef HAVE_HARFBUZZ if (EQ (AREF (font_object, FONT_TYPE_INDEX), Qftcrhb)) font->driver = &ftcrhbfont_driver; else #endif /* HAVE_HARFBUZZ */ font->driver = &ftcrfont_driver; font->encoding_charset = font->repertory_charset = -1; ftcrfont_info = (struct font_info *) font; ftcrfont_info->cr_scaled_font = scaled_font; /* This means that there's no need of transformation. */ ftcrfont_info->matrix.xx = 0; if (FcPatternGetMatrix (match, FC_MATRIX, 0, &matrix) == FcResultMatch) { ftcrfont_info->matrix.xx = 0x10000L * matrix->xx; ftcrfont_info->matrix.yy = 0x10000L * matrix->yy; ftcrfont_info->matrix.xy = 0x10000L * matrix->xy; ftcrfont_info->matrix.yx = 0x10000L * matrix->yx; } ftcrfont_info->metrics = NULL; ftcrfont_info->metrics_nrows = 0; block_input (); cairo_glyph_t stack_glyph; font->min_width = font->average_width = font->space_width = 0; for (char c = 32; c < 127; c++) { cairo_glyph_t *glyphs = &stack_glyph; int num_glyphs = 1; cairo_status_t status = cairo_scaled_font_text_to_glyphs (ftcrfont_info->cr_scaled_font, 0, 0, &c, 1, &glyphs, &num_glyphs, NULL, NULL, NULL); /* In order to simulate the Xft behavior, we use metrics of glyph ID 0 if there is no glyph for an ASCII printable. */ if (status != CAIRO_STATUS_SUCCESS) stack_glyph.index = 0; else if (glyphs != &stack_glyph) { cairo_glyph_free (glyphs); stack_glyph.index = 0; } int this_width = ftcrfont_glyph_extents (font, stack_glyph.index, NULL); if (this_width > 0 && (! font->min_width || font->min_width > this_width)) font->min_width = this_width; if (c == 32) font->space_width = this_width; font->average_width += this_width; } font->average_width /= 95; cairo_scaled_font_extents (ftcrfont_info->cr_scaled_font, &extents); font->ascent = ceil (extents.ascent); val = assq_no_quit (QCminspace, AREF (entity, FONT_EXTRA_INDEX)); if (!(CONSP (val) && NILP (XCDR (val)))) { font->descent = lround (extents.descent); font->height = font->ascent + font->descent; } else { font->height = lround (extents.height); font->descent = font->height - font->ascent; } ft_face = cairo_ft_scaled_font_lock_face (scaled_font); if (XFIXNUM (AREF (entity, FONT_SIZE_INDEX)) == 0) { int upEM = ft_face->units_per_EM; font->underline_position = -ft_face->underline_position * size / upEM; font->underline_thickness = ft_face->underline_thickness * size / upEM; if (font->underline_thickness > 2) font->underline_position -= font->underline_thickness / 2; } else { font->underline_position = -1; font->underline_thickness = 0; } #ifdef HAVE_LIBOTF ftcrfont_info->maybe_otf = (ft_face->face_flags & FT_FACE_FLAG_SFNT) != 0; ftcrfont_info->otf = NULL; #endif /* HAVE_LIBOTF */ #ifdef HAVE_HARFBUZZ ftcrfont_info->hb_font = NULL; #endif /* HAVE_HARFBUZZ */ if (ft_face->units_per_EM) ftcrfont_info->bitmap_position_unit = 0; else ftcrfont_info->bitmap_position_unit = (extents.height / ft_face->size->metrics.height); cairo_ft_scaled_font_unlock_face (scaled_font); ftcrfont_info->ft_size = NULL; unblock_input (); font->baseline_offset = 0; font->relative_compose = 0; font->default_ascent = 0; font->vertical_centering = false; return font_object; } static void ftcrfont_close (struct font *font) { if (font_data_structures_may_be_ill_formed ()) return; struct font_info *ftcrfont_info = (struct font_info *) font; block_input (); #ifdef HAVE_LIBOTF if (ftcrfont_info->otf) { OTF_close (ftcrfont_info->otf); ftcrfont_info->otf = NULL; } #endif #ifdef HAVE_HARFBUZZ if (ftcrfont_info->hb_font) { hb_font_destroy (ftcrfont_info->hb_font); ftcrfont_info->hb_font = NULL; } #endif for (int i = 0; i < ftcrfont_info->metrics_nrows; i++) if (ftcrfont_info->metrics[i]) xfree (ftcrfont_info->metrics[i]); if (ftcrfont_info->metrics) xfree (ftcrfont_info->metrics); cairo_scaled_font_destroy (ftcrfont_info->cr_scaled_font); unblock_input (); } static int ftcrfont_has_char (Lisp_Object font, int c) { if (FONT_ENTITY_P (font)) return ftfont_has_char (font, c); struct charset *cs = NULL; if (EQ (AREF (font, FONT_ADSTYLE_INDEX), Qja) && charset_jisx0208 >= 0) cs = CHARSET_FROM_ID (charset_jisx0208); else if (EQ (AREF (font, FONT_ADSTYLE_INDEX), Qko) && charset_ksc5601 >= 0) cs = CHARSET_FROM_ID (charset_ksc5601); if (cs) return (ENCODE_CHAR (cs, c) != CHARSET_INVALID_CODE (cs)); return -1; } static Lisp_Object ftcrfont_guess_script (struct font *font, int c) { block_input (); hb_buffer_t *hb_buf = hb_buffer_create (); hb_buffer_set_content_type (hb_buf, HB_BUFFER_CONTENT_TYPE_UNICODE); hb_buffer_add (hb_buf, c, 0); hb_buffer_guess_segment_properties (hb_buf); /* Force the language to a known and system-independent value, so as not to make Emacs's behavior depend on LC_CTYPE. */ hb_buffer_set_language (hb_buf, hb_language_from_string ("en", -1)); hb_script_t script = hb_buffer_get_script (hb_buf); Lisp_Object ret; if (script == HB_SCRIPT_UNKNOWN || script == HB_SCRIPT_COMMON || script == HB_SCRIPT_INHERITED || script == HB_SCRIPT_INVALID) ret = Qnil; else { char string[5]; string[4] = 0; string[3] = script & 0xff; string[2] = (script>>8) & 0xff; string[1] = (script>>16) & 0xff; string[0] = (script>>24) & 0xff; ret = build_string (string); } unblock_input (); return ret; } static hb_codepoint_t hb_mirror (hb_unicode_funcs_t *ufuncs, hb_codepoint_t codepoint, void *user_data) { /* HarfBuzz expects unmirrored codepoints, so if we mirrored it, don't mirror it again. */ if (bidi_mirror_char (codepoint) != codepoint) return codepoint; return hb_unicode_mirroring (hb_unicode_funcs_get_parent (ufuncs), codepoint); } static Lisp_Object get_shaped_context (struct font *font, Lisp_Object string, Lisp_Object script) { struct font_info *ftcrfont_info = (struct font_info *) font; if (NILP (font->hb_cache)) font->hb_cache = CALLN (Fmake_hash_table, QCtest, Qequal, QCweakness, Qkey); Lisp_Object cached = Fgethash (Fcons (string, script), font->hb_cache, Qnil); if (!NILP (cached)) return cached; block_input (); unsigned int num_glyphs = 0; hb_buffer_t *hb_buf = hb_buffer_create (); hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_create (hb_buffer_get_unicode_funcs (hb_buf)); hb_unicode_funcs_set_mirroring_func (ufuncs, hb_mirror, NULL, NULL); hb_buffer_set_unicode_funcs (hb_buf, ufuncs); hb_buffer_set_cluster_level (hb_buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); bool rtl = false; for (int i = 0; i < SCHARS (string); i++) { int c = XFIXNUM (Faref (string, make_fixnum (i))); hb_buffer_add (hb_buf, c, i); } hb_buffer_set_content_type (hb_buf, HB_BUFFER_CONTENT_TYPE_UNICODE); if (STRINGP (script) && SBYTES (script) >= 4) { hb_script_t scriptint = 0; scriptint += SREF (script, 0) << 24; scriptint += SREF (script, 1) << 16; scriptint += SREF (script, 2) << 8; scriptint += SREF (script, 3); hb_buffer_set_script (hb_buf, scriptint); } hb_buffer_guess_segment_properties (hb_buf); /* Force the language to a known and system-independent value, so as not to make Emacs's behavior depend on LC_CTYPE. */ hb_buffer_set_language (hb_buf, hb_language_from_string ("en", -1)); rtl = hb_buffer_get_direction (hb_buf) == HB_DIRECTION_RTL; hb_font_t *hb_font = hb_ft_font_create_referenced (cairo_ft_scaled_font_lock_face (ftcrfont_info->cr_scaled_font)); hb_shape (hb_font, hb_buf, NULL, 0); hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos (hb_buf, &num_glyphs); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions (hb_buf, &num_glyphs); /* Reverse glyphs for RTL. This is because HarfBuzz reverses them, not because we want to bypass the code in bidi.c. */ if (rtl) { for (int i = 0; i < num_glyphs/2; i++) { hb_glyph_position_t temp_pos = glyph_pos[num_glyphs - i - 1]; glyph_pos[num_glyphs - i - 1] = glyph_pos[i]; glyph_pos[i] = temp_pos; hb_glyph_info_t temp_info = glyph_info[num_glyphs - i - 1]; glyph_info[num_glyphs - i - 1] = glyph_info[i]; glyph_info[i] = temp_info; } } /* Merge non-spacing glyphs into the previous cluster. */ int last_spacing_cluster = 0; for (int i = 0; i < num_glyphs; i++) { if (glyph_pos[i].x_advance == 0 && glyph_info[i].cluster > last_spacing_cluster) glyph_info[i].cluster = last_spacing_cluster; else last_spacing_cluster = glyph_info[i].cluster; } /* For RTL, un-reverse glyphs that ended up forming a cluster. */ if (rtl) { for (int i = 0; i < num_glyphs;) { int j; for (j = i + 1; j < num_glyphs; j++) { if (glyph_info[j].cluster != glyph_info[i].cluster) break; } for (int k = i; k < i + (j - i)/2; k++) { int k2 = i + j - k - 1; hb_glyph_position_t temp_pos = glyph_pos[k2]; glyph_pos[k2] = glyph_pos[k]; glyph_pos[k] = temp_pos; hb_glyph_info_t temp_info = glyph_info[k2]; glyph_info[k2] = glyph_info[k]; glyph_info[k] = temp_info; } i = j; } } Lisp_Object ret = make_nil_vector (num_glyphs); for (int i = 0; i < num_glyphs; i++) { Lisp_Object glyph = make_nil_vector (HB_SHAPED_LENGTH); ASET (glyph, HB_SHAPED_CODE, make_fixnum (glyph_info[i].codepoint)); ASET (glyph, HB_SHAPED_CLUSTER, make_fixnum (glyph_info[i].cluster)); { Lisp_Object v = make_nil_vector (HB_SHAPED_METRICS_LENGTH); ASET (v, HB_SHAPED_METRICS_X_ADVANCE, make_fixnum ((glyph_pos[i].x_advance + 32)/ 64)); ASET (v, HB_SHAPED_METRICS_Y_ADVANCE, make_fixnum ((glyph_pos[i].y_advance + 32) / 64)); ASET (v, HB_SHAPED_METRICS_X_OFFSET, make_fixnum ((glyph_pos[i].x_offset) / 64)); ASET (v, HB_SHAPED_METRICS_Y_OFFSET, make_fixnum ((glyph_pos[i].y_offset) / 64)); ASET (glyph, HB_SHAPED_METRICS, v); } #if 0 Lisp_Object v = Qnil; if (false) { hb_position_t carets[1024]; unsigned int caret_count = 1022; if (hb_ot_layout_get_ligature_carets (hb_font, HB_DIRECTION_LTR /* XXX */, glyph_info[i].codepoint, 0, &caret_count, carets + 1)) { carets[0] = 0; caret_count++; carets[caret_count++] = glyph_pos[i].x_advance; v = make_nil_vector (caret_count); for (ptrdiff_t i = 0; i < caret_count; i++) ASET (v, i, make_fixnum (carets[i])); } } #endif ASET (glyph, HB_SHAPED_CARETS, Qnil); ASET (glyph, HB_SHAPED_RTL, rtl ? Qt : Qnil); ASET (ret, i, glyph); } Fputhash (Fcons (string, script), ret, font->hb_cache); unblock_input (); return ret; } static Lisp_Object ftcrfont_encode_char (struct font *font, int c, struct glyph_context *context) { Lisp_Object code = Qnil; if (context == NULL) { context = ALLOCATE_PSEUDOVECTOR (struct glyph_context, position, PVEC_GLYPH_CONTEXT); context->script = Qnil; context->string = CALLN (Fstring, make_fixnum (c)); context->position = make_fixnum (0); } bool rtl = false; Lisp_Object shaped = get_shaped_context (font, context->string, context->script); unsigned int num_glyphs = ASIZE (shaped); rtl = ! NILP (AREF (AREF (shaped, 0), HB_SHAPED_RTL)); int glyph0 = 0, glyph1 = num_glyphs; int cluster0 = 0, cluster1 = XFIXNUM (Flength (context->string)); int char0 = 0, char1 = XFIXNUM (Flength (context->string)); for (int i = 0; i < num_glyphs; i++) if (XFIXNUM (AREF (AREF (shaped, i), HB_SHAPED_CLUSTER)) <= XFIXNUM (context->position)) { cluster0 = XFIXNUM (AREF (AREF (shaped, i), HB_SHAPED_CLUSTER)); int i1 = i; while (i1 > 0 && XFIXNUM (AREF (AREF (shaped, i1 - 1), HB_SHAPED_CLUSTER)) == cluster0) i1--; glyph0 = i1; char0 = cluster0; } for (int i = 0; i < num_glyphs; i++) if (XFIXNUM (AREF (AREF (shaped, i), HB_SHAPED_CLUSTER)) > XFIXNUM (context->position)) { glyph1 = i; cluster1 = XFIXNUM (AREF (AREF (shaped, i), HB_SHAPED_CLUSTER)); char1 = cluster1; break; } int i = XFIXNUM (context->position) - char0; if (rtl) i = char1 - char0 - 1 - i; Lisp_Object glyphs = make_nil_vector (glyph1 - glyph0); int x_advance = 0; for (ptrdiff_t i = glyph0; i < glyph1; i++) { Lisp_Object glyph = make_nil_vector (HB_GLYPH_LENGTH); ASET (glyph, HB_GLYPH_GLYPH, Qglyph); Lisp_Object code = AREF (AREF (shaped, i), HB_SHAPED_CODE); /* This appears to be the only sign from HarfBuzz that our font is missing a glyph ... */ if (XFIXNUM (code) == 0) return Qnil; ASET (glyph, HB_GLYPH_CODE, AREF (AREF (shaped, i), HB_SHAPED_CODE)); ASET (glyph, HB_GLYPH_X_OFF, AREF (AREF (AREF (shaped, i), 2), 2)); ASET (glyph, HB_GLYPH_Y_OFF, CALLN (Fminus, AREF (AREF (AREF (shaped, i), 2), 3))); ASET (glyph, HB_GLYPH_CODEPOINT, make_fixnum (c)); ASET (glyph, HB_GLYPH_X_ADVANCE, AREF (AREF (AREF (shaped, i), 2), 0)); ASET (glyphs, i - glyph0, glyph); x_advance += XFIXNUM (AREF (AREF (AREF (shaped, i), 2), 0)); } code = make_nil_vector (HB_SLICE_LENGTH); ASET (code, HB_SLICE_SLICE, Qslice); ASET (code, HB_SLICE_I, make_fixnum (i)); ASET (code, HB_SLICE_N, make_fixnum (char1 - char0)); int x0 = (char1 - char0 - i - 1) * x_advance / (char1 - char0); int x1 = (char1 - char0 - i) * x_advance / (char1 - char0); ASET (code, HB_SLICE_X_ADVANCE, make_fixnum (x1 - x0)); ASET (code, HB_SLICE_X_OFFSET, make_fixnum (x_advance - x1)); ASET (code, HB_SLICE_GLYPHS, glyphs); return code; } static void ftcrfont_text_extents (struct font *font, const Lisp_Object *code, int nglyphs, struct font_metrics *metrics) { int width = 0; block_input (); if (metrics) { for (int i = 0; i < nglyphs; i++) { if (VECTORP (code[i]) && EQ (AREF (code[i], 0), Qslice)) { int slice_ascent = 0; int slice_descent = 0; int slice_lbearing = 0; int slice_rbearing = 0; int slice_i = XFIXNUM (AREF (code[i], HB_SLICE_I)); int slice_n = XFIXNUM (AREF (code[i], HB_SLICE_N)); Lisp_Object glyphs = AREF (code[i], HB_SLICE_GLYPHS); int slice_width = 0; for (ptrdiff_t gi = 0; gi < ASIZE (glyphs); gi++) { Lisp_Object glyph = AREF (glyphs, gi); struct font_metrics m; ftcrfont_glyph_extents (font, XFIXNUM (AREF (glyph, HB_GLYPH_CODE)), &m); int glyph_lbearing = slice_width + m.lbearing + XFIXNUM (AREF (glyph, HB_GLYPH_X_OFF)); int glyph_rbearing = slice_width + m.rbearing + XFIXNUM (AREF (glyph, HB_GLYPH_X_OFF)); int glyph_ascent = m.ascent + XFIXNUM (AREF (glyph, HB_GLYPH_Y_OFF)); // XXX -? int glyph_descent = m.descent + XFIXNUM (AREF (glyph, HB_GLYPH_Y_OFF)); // XXX -? if (gi == 0 || glyph_lbearing < slice_lbearing) slice_lbearing = glyph_lbearing; if (gi == 0 || glyph_rbearing > slice_rbearing) slice_rbearing = glyph_rbearing; if (gi == 0 || glyph_ascent > slice_ascent) slice_ascent = glyph_ascent; if (gi == 0 || glyph_descent > slice_descent) slice_descent = glyph_descent; slice_width += XFIXNUM (AREF (glyph, HB_GLYPH_X_ADVANCE)); } slice_width = XFIXNUM (AREF (code[i], HB_SLICE_X_ADVANCE)); if (slice_i < slice_n - 1) slice_rbearing = XFIXNUM (AREF (code[i], HB_SLICE_X_ADVANCE)) + XFIXNUM (AREF (code[i], HB_SLICE_X_OFFSET)); if (slice_i > 0) slice_lbearing = XFIXNUM (AREF (code[i], HB_SLICE_X_OFFSET)); if (i == 0 || slice_lbearing < metrics->lbearing) metrics->lbearing = slice_lbearing; if (i == 0 || slice_rbearing > metrics->rbearing) metrics->rbearing = slice_rbearing; if (i == 0 || slice_ascent > metrics->ascent) metrics->ascent = slice_ascent; if (i == 0 || slice_descent > metrics->descent) metrics->descent = slice_descent; width += slice_width; } else if (FIXNUMP (code[i])) { } } metrics->width = width; } unblock_input (); } static int ftcrfont_get_bitmap (struct font *font, unsigned int code, struct font_bitmap *bitmap, int bits_per_pixel) { struct font_info *ftcrfont_info = (struct font_info *) font; if (ftcrfont_info->bitmap_position_unit) return -1; cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font; FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); ftcrfont_info->ft_size = ft_face->size; int result = ftfont_get_bitmap (font, code, bitmap, bits_per_pixel); cairo_ft_scaled_font_unlock_face (scaled_font); ftcrfont_info->ft_size = NULL; return result; } static int ftcrfont_anchor_point (struct font *font, unsigned int code, int idx, int *x, int *y) { struct font_info *ftcrfont_info = (struct font_info *) font; if (ftcrfont_info->bitmap_position_unit) return -1; cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font; FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); ftcrfont_info->ft_size = ft_face->size; int result = ftfont_anchor_point (font, code, idx, x, y); cairo_ft_scaled_font_unlock_face (scaled_font); ftcrfont_info->ft_size = NULL; return result; } #ifdef HAVE_LIBOTF static Lisp_Object ftcrfont_otf_capability (struct font *font) { struct font_info *ftcrfont_info = (struct font_info *) font; cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font; FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); ftcrfont_info->ft_size = ft_face->size; Lisp_Object result = ftfont_otf_capability (font); cairo_ft_scaled_font_unlock_face (scaled_font); ftcrfont_info->ft_size = NULL; return result; } #endif #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF static Lisp_Object ftcrfont_shape (Lisp_Object lgstring, Lisp_Object direction) { struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring)); struct font_info *ftcrfont_info = (struct font_info *) font; if (ftcrfont_info->bitmap_position_unit) return make_fixnum (0); cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font; FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); ftcrfont_info->ft_size = ft_face->size; Lisp_Object result = ftfont_shape (lgstring, direction); cairo_ft_scaled_font_unlock_face (scaled_font); ftcrfont_info->ft_size = NULL; return result; } #endif #if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined HAVE_FT_FACE_GETCHARVARIANTINDEX static int ftcrfont_variation_glyphs (struct font *font, int c, unsigned variations[256]) { struct font_info *ftcrfont_info = (struct font_info *) font; cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font; FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); ftcrfont_info->ft_size = ft_face->size; int result = ftfont_variation_glyphs (font, c, variations); cairo_ft_scaled_font_unlock_face (scaled_font); ftcrfont_info->ft_size = NULL; return result; } #endif /* HAVE_OTF_GET_VARIATION_GLYPHS || HAVE_FT_FACE_GETCHARVARIANTINDEX */ static int ftcrfont_draw (struct glyph_string *s, int from, int to, int x, int y, bool with_background) { struct frame *f = s->f; struct face *face = s->face; struct font_info *ftcrfont_info = (struct font_info *) s->font; cairo_t *cr; int len = to - from; int i; block_input (); cr = x_begin_cr_clip (f, s->gc); if (with_background) { x_set_cr_source_with_gc_background (f, s->gc); cairo_rectangle (cr, x, y - FONT_BASE (face->font), s->width, FONT_HEIGHT (face->font)); cairo_fill (cr); } x_set_cr_source_with_gc_foreground (f, s->gc); cairo_set_scaled_font (cr, ftcrfont_info->cr_scaled_font); for (i = 0; i < len; i++) { Lisp_Object code = s->char2b[from + i]; if (VECTORP (code)) { if (EQ (AREF (code, 0), Qslice)) { int xtmp = x; cairo_save (cr); struct font_metrics metrics; ftcrfont_text_extents (s->font, &code, 1, &metrics); double x0 = x + metrics.lbearing - XFIXNUM (AREF (code, HB_SLICE_X_OFFSET)); double x1 = x + metrics.rbearing - XFIXNUM (AREF (code, HB_SLICE_X_OFFSET)); double y0 = y - metrics.ascent; double y1 = y + metrics.descent; x0--; y0--; x1++; y1++; /* FIXME */ cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0); cairo_clip (cr); Lisp_Object glyphs = AREF (code, HB_SLICE_GLYPHS); for (ptrdiff_t gi = 0; gi < ASIZE (glyphs); gi++) { Lisp_Object glyph = AREF (glyphs, gi); cairo_glyph_t cglyph; cglyph.index = XFIXNUM (AREF (glyph, HB_GLYPH_CODE)); cglyph.x = x + XFIXNUM (AREF (glyph, HB_GLYPH_X_OFF)); cglyph.y = y + XFIXNUM (AREF (glyph, HB_GLYPH_Y_OFF)); cglyph.x -= XFIXNUM (AREF (code, HB_SLICE_X_OFFSET)); x += XFIXNUM (AREF (glyph, HB_GLYPH_X_ADVANCE)); cairo_show_glyphs (cr, &cglyph, 1); } cairo_restore (cr); x = xtmp + XFIXNUM (AREF (code, HB_SLICE_X_ADVANCE)); } } else if (FIXNUMP (code)) { cairo_glyph_t cglyph; cglyph.index = XFIXNUM (code); cglyph.x = x; cglyph.y = y; x += (s->padding_p ? 0 : 0); cairo_show_glyphs (cr, &cglyph, 1); } } x_end_cr_clip (f); unblock_input (); return len; } #ifdef HAVE_HARFBUZZ static Lisp_Object ftcrhbfont_list (struct frame *f, Lisp_Object spec) { return ftfont_list2 (f, spec, Qftcrhb); } static Lisp_Object ftcrhbfont_match (struct frame *f, Lisp_Object spec) { return ftfont_match2 (f, spec, Qftcrhb); } static hb_font_t * ftcrhbfont_begin_hb_font (struct font *font, double *position_unit) { struct font_info *ftcrfont_info = (struct font_info *) font; cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font; FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); ftcrfont_info->ft_size = ft_face->size; hb_font_t *hb_font = fthbfont_begin_hb_font (font, position_unit); if (ftcrfont_info->bitmap_position_unit) *position_unit = ftcrfont_info->bitmap_position_unit; return hb_font; } static void ftcrhbfont_end_hb_font (struct font *font, hb_font_t *hb_font) { struct font_info *ftcrfont_info = (struct font_info *) font; cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font; cairo_ft_scaled_font_unlock_face (scaled_font); ftcrfont_info->ft_size = NULL; } #endif /* HAVE_HARFBUZZ */ static void syms_of_ftcrfont_for_pdumper (void); struct font_driver const ftcrfont_driver = { .type = LISPSYM_INITIALLY (Qftcr), .get_cache = ftfont_get_cache, .list = ftcrfont_list, .match = ftcrfont_match, .list_family = ftfont_list_family, .open_font = ftcrfont_open, .close_font = ftcrfont_close, .has_char = ftcrfont_has_char, .encode_char = ftcrfont_encode_char, .text_extents = ftcrfont_text_extents, .draw = ftcrfont_draw, .get_bitmap = ftcrfont_get_bitmap, .anchor_point = ftcrfont_anchor_point, .guess_script = ftcrfont_guess_script, #ifdef HAVE_LIBOTF .otf_capability = ftcrfont_otf_capability, #endif #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF .shape = ftcrfont_shape, #endif #if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined HAVE_FT_FACE_GETCHARVARIANTINDEX .get_variation_glyphs = ftcrfont_variation_glyphs, #endif .filter_properties = ftfont_filter_properties, .combining_capability = ftfont_combining_capability, }; #ifdef HAVE_HARFBUZZ struct font_driver ftcrhbfont_driver; #endif /* HAVE_HARFBUZZ */ void syms_of_ftcrfont (void) { DEFSYM (Qftcr, "ftcr"); #ifdef HAVE_HARFBUZZ DEFSYM (Qftcrhb, "ftcrhb"); Fput (Qftcr, Qfont_driver_superseded_by, Qftcrhb); #endif /* HAVE_HARFBUZZ */ pdumper_do_now_and_after_load (syms_of_ftcrfont_for_pdumper); } static void syms_of_ftcrfont_for_pdumper (void) { register_font_driver (&ftcrfont_driver, NULL); #ifdef HAVE_HARFBUZZ ftcrhbfont_driver = ftcrfont_driver; ftcrhbfont_driver.type = Qftcrhb; ftcrhbfont_driver.list = ftcrhbfont_list; ftcrhbfont_driver.match = ftcrhbfont_match; ftcrhbfont_driver.otf_capability = hbfont_otf_capability; ftcrhbfont_driver.shape = hbfont_shape; ftcrhbfont_driver.combining_capability = hbfont_combining_capability; ftcrhbfont_driver.begin_hb_font = ftcrhbfont_begin_hb_font; ftcrhbfont_driver.end_hb_font = ftcrhbfont_end_hb_font; register_font_driver (&ftcrhbfont_driver, NULL); #endif /* HAVE_HARFBUZZ */ }