/* Font support for Haiku windowing Copyright (C) 2021 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 "lisp.h" #include "dispextern.h" #include "composite.h" #include "blockinput.h" #include "charset.h" #include "frame.h" #include "window.h" #include "fontset.h" #include "haikuterm.h" #include "character.h" #include "font.h" #include "termchar.h" #include "pdumper.h" #include "haiku_support.h" #include #include static Lisp_Object font_cache; #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 struct { /* registry name */ const char *name; /* characters to distinguish the charset from the others */ int uniquifier[6]; /* additional constraint by language */ const char *lang; } em_charset_table[] = { { "iso8859-1", { 0x00A0, 0x00A1, 0x00B4, 0x00BC, 0x00D0 } }, { "iso8859-2", { 0x00A0, 0x010E }}, { "iso8859-3", { 0x00A0, 0x0108 }}, { "iso8859-4", { 0x00A0, 0x00AF, 0x0128, 0x0156, 0x02C7 }}, { "iso8859-5", { 0x00A0, 0x0401 }}, { "iso8859-6", { 0x00A0, 0x060C }}, { "iso8859-7", { 0x00A0, 0x0384 }}, { "iso8859-8", { 0x00A0, 0x05D0 }}, { "iso8859-9", { 0x00A0, 0x00A1, 0x00BC, 0x011E }}, { "iso8859-10", { 0x00A0, 0x00D0, 0x0128, 0x2015 }}, { "iso8859-11", { 0x00A0, 0x0E01 }}, { "iso8859-13", { 0x00A0, 0x201C }}, { "iso8859-14", { 0x00A0, 0x0174 }}, { "iso8859-15", { 0x00A0, 0x00A1, 0x00D0, 0x0152 }}, { "iso8859-16", { 0x00A0, 0x0218}}, { "gb2312.1980-0", { 0x4E13 }, "zh-cn"}, { "big5-0", { 0x9C21 }, "zh-tw" }, { "jisx0208.1983-0", { 0x4E55 }, "ja"}, { "ksc5601.1985-0", { 0xAC00 }, "ko"}, { "cns11643.1992-1", { 0xFE32 }, "zh-tw"}, { "cns11643.1992-2", { 0x4E33, 0x7934 }}, { "cns11643.1992-3", { 0x201A9 }}, { "cns11643.1992-4", { 0x20057 }}, { "cns11643.1992-5", { 0x20000 }}, { "cns11643.1992-6", { 0x20003 }}, { "cns11643.1992-7", { 0x20055 }}, { "gbk-0", { 0x4E06 }, "zh-cn"}, { "jisx0212.1990-0", { 0x4E44 }}, { "jisx0213.2000-1", { 0xFA10 }, "ja"}, { "jisx0213.2000-2", { 0xFA49 }}, { "jisx0213.2004-1", { 0x20B9F }}, { "viscii1.1-1", { 0x1EA0, 0x1EAE, 0x1ED2 }, "vi"}, { "tis620.2529-1", { 0x0E01 }, "th"}, { "microsoft-cp1251", { 0x0401, 0x0490 }, "ru"}, { "koi8-r", { 0x0401, 0x2219 }, "ru"}, { "mulelao-1", { 0x0E81 }, "lo"}, { "unicode-sip", { 0x20000 }}, { "mulearabic-0", { 0x628 }}, { "mulearabic-1", { 0x628 }}, { "mulearabic-2", { 0x628 }}, { NULL } }; static void haikufont_apply_registry (struct haiku_font_pattern *pattern, Lisp_Object registry) { char *str = SSDATA (SYMBOL_NAME (registry)); USE_SAFE_ALLOCA; char *re = SAFE_ALLOCA (SBYTES (SYMBOL_NAME (registry)) * 2 + 1); int i, j; for (i = j = 0; i < SBYTES (SYMBOL_NAME (registry)); i++, j++) { if (str[i] == '.') re[j++] = '\\'; else if (str[i] == '*') re[j++] = '.'; re[j] = str[i]; if (re[j] == '?') re[j] = '.'; } re[j] = '\0'; AUTO_STRING_WITH_LEN (regexp, re, j); for (i = 0; em_charset_table[i].name; i++) if (fast_c_string_match_ignore_case (regexp, em_charset_table[i].name, strlen (em_charset_table[i].name)) >= 0) break; SAFE_FREE (); if (!em_charset_table[i].name) return; int *uniquifier = em_charset_table[i].uniquifier; int l; for (l = 0; uniquifier[l]; ++l); uint32_t *a = xmalloc (l * sizeof *a); for (l = 0; uniquifier[l]; ++l) a[l] = uniquifier[l]; if (pattern->specified & FSPEC_WANTED) { int old_l = l; l += pattern->want_chars_len; a = xrealloc (a, l * sizeof *a); memcpy (&a[old_l], pattern->wanted_chars, (l - old_l) * sizeof *a); xfree (pattern->wanted_chars); } pattern->specified |= FSPEC_WANTED; pattern->want_chars_len = l; pattern->wanted_chars = a; if (em_charset_table[i].lang) { if (!strncmp (em_charset_table[i].lang, "zh", 2)) { pattern->specified |= FSPEC_LANGUAGE; pattern->language = LANGUAGE_CN; } else if (!strncmp (em_charset_table[i].lang, "ko", 2)) { pattern->specified |= FSPEC_LANGUAGE; pattern->language = LANGUAGE_KO; } else if (!strncmp (em_charset_table[i].lang, "ja", 2)) { pattern->specified |= FSPEC_LANGUAGE; pattern->language = LANGUAGE_JP; } } return; } static Lisp_Object haikufont_get_fallback_entity (void) { Lisp_Object ent = font_make_entity (); ASET (ent, FONT_TYPE_INDEX, Qhaiku); ASET (ent, FONT_FOUNDRY_INDEX, Qhaiku); ASET (ent, FONT_FAMILY_INDEX, Qnil); ASET (ent, FONT_ADSTYLE_INDEX, Qnil); ASET (ent, FONT_REGISTRY_INDEX, Qutf_8); ASET (ent, FONT_SIZE_INDEX, make_fixnum (0)); ASET (ent, FONT_AVGWIDTH_INDEX, make_fixnum (0)); ASET (ent, FONT_SPACING_INDEX, make_fixnum (FONT_SPACING_MONO)); FONT_SET_STYLE (ent, FONT_WIDTH_INDEX, Qnil); FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX, Qnil); FONT_SET_STYLE (ent, FONT_SLANT_INDEX, Qnil); return ent; } static Lisp_Object haikufont_get_cache (struct frame *frame) { return font_cache; } static Lisp_Object haikufont_weight_to_lisp (int weight) { switch (weight) { case HAIKU_THIN: return Qthin; case HAIKU_ULTRALIGHT: return Qultra_light; case HAIKU_EXTRALIGHT: return Qextra_light; case HAIKU_LIGHT: return Qlight; case HAIKU_SEMI_LIGHT: return Qsemi_light; case HAIKU_REGULAR: return Qnormal; case HAIKU_SEMI_BOLD: return Qsemi_bold; case HAIKU_BOLD: return Qbold; case HAIKU_EXTRA_BOLD: return Qextra_bold; case HAIKU_ULTRA_BOLD: return Qultra_bold; case HAIKU_BOOK: return Qbook; case HAIKU_HEAVY: return Qheavy; case HAIKU_ULTRA_HEAVY: return Qultra_heavy; case HAIKU_BLACK: return Qblack; case HAIKU_MEDIUM: return Qmedium; } emacs_abort (); } static int haikufont_lisp_to_weight (Lisp_Object weight) { if (EQ (weight, Qthin)) return HAIKU_THIN; if (EQ (weight, Qultra_light)) return HAIKU_ULTRALIGHT; if (EQ (weight, Qextra_light)) return HAIKU_EXTRALIGHT; if (EQ (weight, Qlight)) return HAIKU_LIGHT; if (EQ (weight, Qsemi_light)) return HAIKU_SEMI_LIGHT; if (EQ (weight, Qnormal)) return HAIKU_REGULAR; if (EQ (weight, Qsemi_bold)) return HAIKU_SEMI_BOLD; if (EQ (weight, Qbold)) return HAIKU_BOLD; if (EQ (weight, Qextra_bold)) return HAIKU_EXTRA_BOLD; if (EQ (weight, Qultra_bold)) return HAIKU_ULTRA_BOLD; if (EQ (weight, Qbook)) return HAIKU_BOOK; if (EQ (weight, Qheavy)) return HAIKU_HEAVY; if (EQ (weight, Qultra_heavy)) return HAIKU_ULTRA_HEAVY; if (EQ (weight, Qblack)) return HAIKU_BLACK; if (EQ (weight, Qmedium)) return HAIKU_MEDIUM; emacs_abort (); } static Lisp_Object haikufont_slant_to_lisp (enum haiku_font_slant slant) { switch (slant) { case NO_SLANT: emacs_abort (); case SLANT_ITALIC: return Qitalic; case SLANT_REGULAR: return Qnormal; case SLANT_OBLIQUE: return Qoblique; } emacs_abort (); } static enum haiku_font_slant haikufont_lisp_to_slant (Lisp_Object slant) { if (EQ (slant, Qitalic) || EQ (slant, Qreverse_italic)) return SLANT_ITALIC; if (EQ (slant, Qoblique) || EQ (slant, Qreverse_oblique)) return SLANT_OBLIQUE; if (EQ (slant, Qnormal)) return SLANT_REGULAR; emacs_abort (); } static Lisp_Object haikufont_width_to_lisp (enum haiku_font_width width) { switch (width) { case NO_WIDTH: emacs_abort (); case ULTRA_CONDENSED: return Qultra_condensed; case EXTRA_CONDENSED: return Qextra_condensed; case CONDENSED: return Qcondensed; case SEMI_CONDENSED: return Qsemi_condensed; case NORMAL_WIDTH: return Qnormal; case SEMI_EXPANDED: return Qsemi_expanded; case EXPANDED: return Qexpanded; case EXTRA_EXPANDED: return Qextra_expanded; case ULTRA_EXPANDED: return Qultra_expanded; } emacs_abort (); } static enum haiku_font_width haikufont_lisp_to_width (Lisp_Object lisp) { if (EQ (lisp, Qultra_condensed)) return ULTRA_CONDENSED; if (EQ (lisp, Qextra_condensed)) return EXTRA_CONDENSED; if (EQ (lisp, Qcondensed)) return CONDENSED; if (EQ (lisp, Qsemi_condensed)) return SEMI_CONDENSED; if (EQ (lisp, Qnormal)) return NORMAL_WIDTH; if (EQ (lisp, Qexpanded)) return EXPANDED; if (EQ (lisp, Qextra_expanded)) return EXTRA_EXPANDED; if (EQ (lisp, Qultra_expanded)) return ULTRA_EXPANDED; emacs_abort (); } static int haikufont_maybe_handle_special_family (Lisp_Object family, struct haiku_font_pattern *ptn) { CHECK_SYMBOL (family); if (EQ (family, Qmonospace) || EQ (family, Qfixed) || EQ (family, Qdefault)) { BFont_populate_fixed_family (ptn); return 1; } else if (EQ (family, intern ("Sans Serif"))) { BFont_populate_plain_family (ptn); return 1; } return 0; } static Lisp_Object haikufont_pattern_to_entity (struct haiku_font_pattern *ptn) { Lisp_Object ent = font_make_entity (); ASET (ent, FONT_TYPE_INDEX, Qhaiku); ASET (ent, FONT_FOUNDRY_INDEX, Qhaiku); ASET (ent, FONT_FAMILY_INDEX, Qdefault); ASET (ent, FONT_ADSTYLE_INDEX, Qnil); ASET (ent, FONT_REGISTRY_INDEX, Qutf_8); ASET (ent, FONT_SIZE_INDEX, make_fixnum (0)); ASET (ent, FONT_AVGWIDTH_INDEX, make_fixnum (0)); ASET (ent, FONT_SPACING_INDEX, make_fixnum (FONT_SPACING_MONO)); FONT_SET_STYLE (ent, FONT_WIDTH_INDEX, Qnormal); FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX, Qnormal); FONT_SET_STYLE (ent, FONT_SLANT_INDEX, Qnormal); if (ptn->specified & FSPEC_FAMILY) ASET (ent, FONT_FAMILY_INDEX, intern (ptn->family)); else ASET (ent, FONT_FAMILY_INDEX, Qdefault); if (ptn->specified & FSPEC_STYLE) ASET (ent, FONT_ADSTYLE_INDEX, intern (ptn->style)); else { if (ptn->specified & FSPEC_WEIGHT) FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX, haikufont_weight_to_lisp (ptn->weight)); if (ptn->specified & FSPEC_SLANT) FONT_SET_STYLE (ent, FONT_SLANT_INDEX, haikufont_slant_to_lisp (ptn->slant)); if (ptn->specified & FSPEC_WIDTH) FONT_SET_STYLE (ent, FONT_WIDTH_INDEX, haikufont_width_to_lisp (ptn->width)); } if (ptn->specified & FSPEC_SPACING) ASET (ent, FONT_SPACING_INDEX, make_fixnum (ptn->mono_spacing_p ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL)); return ent; } static void haikufont_spec_or_entity_to_pattern (Lisp_Object ent, int list_p, struct haiku_font_pattern *ptn) { Lisp_Object tem; ptn->specified = 0; tem = AREF (ent, FONT_ADSTYLE_INDEX); if (!NILP (tem)) { ptn->specified |= FSPEC_STYLE; strncpy ((char *) &ptn->style, SSDATA (SYMBOL_NAME (tem)), sizeof ptn->style - 1); } tem = FONT_SLANT_SYMBOLIC (ent); if (!NILP (tem)) { ptn->specified |= FSPEC_SLANT; ptn->slant = haikufont_lisp_to_slant (tem); } tem = FONT_WEIGHT_SYMBOLIC (ent); if (!NILP (tem)) { ptn->specified |= FSPEC_WEIGHT; ptn->weight = haikufont_lisp_to_weight (tem); } tem = FONT_WIDTH_SYMBOLIC (ent); if (!NILP (tem)) { ptn->specified |= FSPEC_WIDTH; ptn->width = haikufont_lisp_to_width (tem); } tem = AREF (ent, FONT_SPACING_INDEX); if (FIXNUMP (tem)) { ptn->specified |= FSPEC_SPACING; ptn->mono_spacing_p = XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL; } tem = AREF (ent, FONT_FAMILY_INDEX); if (!NILP (tem) && (list_p && !haikufont_maybe_handle_special_family (tem, ptn))) { ptn->specified |= FSPEC_FAMILY; strncpy ((char *) &ptn->family, SSDATA (SYMBOL_NAME (tem)), sizeof ptn->family - 1); } tem = assq_no_quit (QCscript, AREF (ent, FONT_EXTRA_INDEX)); if (!NILP (tem)) { tem = assq_no_quit (XCDR (tem), Vscript_representative_chars); if (CONSP (tem) && VECTORP (XCDR (tem))) { tem = XCDR (tem); int count = 0; for (int j = 0; j < ASIZE (tem); ++j) if (TYPE_RANGED_FIXNUMP (uint32_t, AREF (tem, j))) ++count; if (count) { ptn->specified |= FSPEC_NEED_ONE_OF; ptn->need_one_of_len = count; ptn->need_one_of = xmalloc (count * sizeof *ptn->need_one_of); count = 0; for (int j = 0; j < ASIZE (tem); ++j) if (TYPE_RANGED_FIXNUMP (uint32_t, AREF (tem, j))) { ptn->need_one_of[j] = XFIXNAT (AREF (tem, j)); ++count; } } } else if (CONSP (tem) && CONSP (XCDR (tem))) { int count = 0; for (Lisp_Object it = XCDR (tem); CONSP (it); it = XCDR (it)) if (TYPE_RANGED_FIXNUMP (uint32_t, XCAR (it))) ++count; if (count) { ptn->specified |= FSPEC_WANTED; ptn->want_chars_len = count; ptn->wanted_chars = xmalloc (count * sizeof *ptn->wanted_chars); count = 0; for (tem = XCDR (tem); CONSP (tem); tem = XCDR (tem)) if (TYPE_RANGED_FIXNUMP (uint32_t, XCAR (tem))) { ptn->wanted_chars[count] = XFIXNAT (XCAR (tem)); ++count; } } } } tem = assq_no_quit (QClang, AREF (ent, FONT_EXTRA_INDEX)); if (CONSP (tem)) { tem = XCDR (tem); if (EQ (tem, Qzh)) { ptn->specified |= FSPEC_LANGUAGE; ptn->language = LANGUAGE_CN; } else if (EQ (tem, Qko)) { ptn->specified |= FSPEC_LANGUAGE; ptn->language = LANGUAGE_KO; } else if (EQ (tem, Qjp)) { ptn->specified |= FSPEC_LANGUAGE; ptn->language = LANGUAGE_JP; } } tem = AREF (ent, FONT_REGISTRY_INDEX); if (SYMBOLP (tem)) haikufont_apply_registry (ptn, tem); } static void haikufont_done_with_query_pattern (struct haiku_font_pattern *ptn) { if (ptn->specified & FSPEC_WANTED) xfree (ptn->wanted_chars); if (ptn->specified & FSPEC_NEED_ONE_OF) xfree (ptn->need_one_of); } static Lisp_Object haikufont_match (struct frame *f, Lisp_Object font_spec) { block_input (); Lisp_Object tem = Qnil; struct haiku_font_pattern ptn; haikufont_spec_or_entity_to_pattern (font_spec, 0, &ptn); ptn.specified &= ~FSPEC_FAMILY; struct haiku_font_pattern *found = BFont_find (&ptn); haikufont_done_with_query_pattern (&ptn); if (found) { tem = haikufont_pattern_to_entity (found); haiku_font_pattern_free (found); } unblock_input (); return !NILP (tem) ? tem : haikufont_get_fallback_entity (); } static Lisp_Object haikufont_list (struct frame *f, Lisp_Object font_spec) { block_input (); Lisp_Object lst = Qnil; /* Returning irrelevant results on receiving an OTF form will cause fontset.c to loop over and over, making displaying some characters very slow. */ Lisp_Object tem = assq_no_quit (QCotf, AREF (font_spec, FONT_EXTRA_INDEX)); if (CONSP (tem) && !NILP (XCDR (tem))) { unblock_input (); return Qnil; } struct haiku_font_pattern ptn; haikufont_spec_or_entity_to_pattern (font_spec, 1, &ptn); struct haiku_font_pattern *found = BFont_find (&ptn); haikufont_done_with_query_pattern (&ptn); if (found) { for (struct haiku_font_pattern *pt = found; pt; pt = pt->next) lst = Fcons (haikufont_pattern_to_entity (pt), lst); haiku_font_pattern_free (found); } unblock_input (); return lst; } static void haiku_bulk_encode (struct haikufont_info *font_info, int block) { unsigned short *unichars = xmalloc (0x101 * sizeof (*unichars)); unsigned int i, idx; block_input (); font_info->glyphs[block] = unichars; if (!unichars) emacs_abort (); for (idx = block << 8, i = 0; i < 0x100; idx++, i++) unichars[i] = idx; unichars[0x100] = 0; /* If the font contains the entire block, just store it. */ if (!BFont_have_char_block (font_info->be_font, unichars[0], unichars[0xff])) { for (int i = 0; i < 0x100; ++i) if (!BFont_have_char_p (font_info->be_font, unichars[i])) unichars[i] = 0xFFFF; } unblock_input (); } static unsigned int haikufont_encode_char (struct font *font, int c) { struct haikufont_info *font_info = (struct haikufont_info *) font; unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff; unsigned short g; if (c > 0xFFFF) return FONT_INVALID_CODE; if (!font_info->glyphs[high]) haiku_bulk_encode (font_info, high); g = font_info->glyphs[high][low]; return g == 0xFFFF ? FONT_INVALID_CODE : g; } static Lisp_Object haikufont_open (struct frame *f, Lisp_Object font_entity, int x) { struct haikufont_info *font_info; struct haiku_font_pattern ptn; struct font *font; void *be_font; Lisp_Object font_object; Lisp_Object tem; block_input (); if (x <= 0) { /* Get pixel size from frame instead. */ tem = get_frame_param (f, Qfontsize); x = NILP (tem) ? 0 : XFIXNAT (tem); } haikufont_spec_or_entity_to_pattern (font_entity, 1, &ptn); if (BFont_open_pattern (&ptn, &be_font, x)) { haikufont_done_with_query_pattern (&ptn); unblock_input (); return Qnil; } haikufont_done_with_query_pattern (&ptn); font_object = font_make_object (VECSIZE (struct haikufont_info), font_entity, x); ASET (font_object, FONT_TYPE_INDEX, Qhaiku); font_info = (struct haikufont_info *) XFONT_OBJECT (font_object); font = (struct font *) font_info; if (!font) { unblock_input (); return Qnil; } font_info->be_font = be_font; font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs); font->pixel_size = 0; font->driver = &haikufont_driver; font->encoding_charset = -1; font->repertory_charset = -1; font->default_ascent = 0; font->vertical_centering = 0; font->baseline_offset = 0; font->relative_compose = 0; font_info->metrics = NULL; font_info->metrics_nrows = 0; int px_size, min_width, max_width, avg_width, height, space_width, ascent, descent, underline_pos, underline_thickness; BFont_dat (be_font, &px_size, &min_width, &max_width, &avg_width, &height, &space_width, &ascent, &descent, &underline_pos, &underline_thickness); font->pixel_size = px_size; font->min_width = min_width; font->max_width = max_width; font->average_width = avg_width; font->height = height; font->space_width = space_width; font->ascent = ascent; font->descent = descent; font->default_ascent = ascent; font->underline_position = underline_pos; font->underline_thickness = underline_thickness; font->vertical_centering = 0; font->baseline_offset = 0; font->relative_compose = 0; font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil); unblock_input (); return font_object; } static void haikufont_close (struct font *font) { if (font_data_structures_may_be_ill_formed ()) return; struct haikufont_info *info = (struct haikufont_info *) font; block_input (); if (info && info->be_font) BFont_close (info->be_font); for (int i = 0; i < info->metrics_nrows; i++) if (info->metrics[i]) xfree (info->metrics[i]); if (info->metrics) xfree (info->metrics); for (int i = 0; i < 0x100; ++i) if (info->glyphs[i]) xfree (info->glyphs[i]); xfree (info->glyphs); unblock_input (); } static void haikufont_prepare_face (struct frame *f, struct face *face) { } static void haikufont_glyph_extents (struct font *font, unsigned code, struct font_metrics *metrics) { struct haikufont_info *info = (struct haikufont_info *) font; struct font_metrics *cache; int row, col; row = code / METRICS_NCOLS_PER_ROW; col = code % METRICS_NCOLS_PER_ROW; if (row >= info->metrics_nrows) { info->metrics = xrealloc (info->metrics, sizeof (struct font_metrics *) * (row + 1)); memset (info->metrics + info->metrics_nrows, 0, (sizeof (struct font_metrics *) * (row + 1 - info->metrics_nrows))); info->metrics_nrows = row + 1; } if (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); info->metrics[row] = new; } cache = info->metrics[row] + col; if (METRICS_STATUS (cache) == METRICS_INVALID) { unsigned char utf8[MAX_MULTIBYTE_LENGTH]; memset (utf8, 0, MAX_MULTIBYTE_LENGTH); CHAR_STRING (code, utf8); int advance, lb, rb; BFont_char_bounds (info->be_font, (const char *) utf8, &advance, &lb, &rb); cache->lbearing = lb; cache->rbearing = rb; cache->width = advance; cache->ascent = font->ascent; cache->descent = font->descent; } if (metrics) *metrics = *cache; } static void haikufont_text_extents (struct font *font, const unsigned int *code, int nglyphs, struct font_metrics *metrics) { int totalwidth = 0; memset (metrics, 0, sizeof (struct font_metrics)); block_input (); for (int i = 0; i < nglyphs; i++) { struct font_metrics m; haikufont_glyph_extents (font, code[i], &m); if (metrics) { if (totalwidth + m.lbearing < metrics->lbearing) metrics->lbearing = totalwidth + m.lbearing; if (totalwidth + m.rbearing > metrics->rbearing) metrics->rbearing = totalwidth + m.rbearing; if (m.ascent > metrics->ascent) metrics->ascent = m.ascent; if (m.descent > metrics->descent) metrics->descent = m.descent; } totalwidth += m.width; } unblock_input (); if (metrics) metrics->width = totalwidth; } static Lisp_Object haikufont_shape (Lisp_Object lgstring, Lisp_Object direction) { struct haikufont_info *font = (struct haikufont_info *) CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring)); int *advance, *lb, *rb; ptrdiff_t glyph_len, len, i, b_len; Lisp_Object tem; char *b; uint32_t *mb_buf; glyph_len = LGSTRING_GLYPH_LEN (lgstring); for (i = 0; i < glyph_len; ++i) { tem = LGSTRING_GLYPH (lgstring, i); if (NILP (tem)) break; } len = i; if (INT_MAX / 2 < len) memory_full (SIZE_MAX); block_input (); b_len = 0; b = xmalloc (b_len); mb_buf = alloca (len * sizeof *mb_buf); for (i = b_len; i < len; ++i) { uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i)); mb_buf[i] = c; unsigned char mb[MAX_MULTIBYTE_LENGTH]; int slen = CHAR_STRING (c, mb); b = xrealloc (b, b_len = (b_len + slen)); if (len == 1) b[b_len - slen] = mb[0]; else memcpy (b + b_len - slen, mb, slen); } advance = alloca (len * sizeof *advance); lb = alloca (len * sizeof *lb); rb = alloca (len * sizeof *rb); eassert (font->be_font); BFont_nchar_bounds (font->be_font, b, advance, lb, rb, len); xfree (b); for (i = 0; i < len; ++i) { tem = LGSTRING_GLYPH (lgstring, i); if (NILP (tem)) { tem = LGLYPH_NEW (); LGSTRING_SET_GLYPH (lgstring, i, tem); } LGLYPH_SET_FROM (tem, i); LGLYPH_SET_TO (tem, i); LGLYPH_SET_CHAR (tem, mb_buf[i]); LGLYPH_SET_CODE (tem, mb_buf[i]); LGLYPH_SET_WIDTH (tem, advance[i]); LGLYPH_SET_LBEARING (tem, lb[i]); LGLYPH_SET_RBEARING (tem, rb[i]); LGLYPH_SET_ASCENT (tem, font->font.ascent); LGLYPH_SET_DESCENT (tem, font->font.descent); } unblock_input (); return make_fixnum (len); } static int haikufont_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 *info = (struct font_info *) s->font; unsigned char mb[MAX_MULTIBYTE_LENGTH]; void *view = FRAME_HAIKU_VIEW (f); block_input (); prepare_face_for_display (s->f, face); BView_draw_lock (view); BView_StartClip (view); if (with_background) { int height = FONT_HEIGHT (s->font), ascent = FONT_BASE (s->font); /* Font's global height and ascent values might be preposterously large for some fonts. We fix here the case when those fonts are used for display of glyphless characters, because drawing background with font dimensions in those cases makes the display illegible. There's only one more call to the draw method with with_background set to true, and that's in x_draw_glyph_string_foreground, when drawing the cursor, where we have no such heuristics available. FIXME. */ if (s->first_glyph->type == GLYPHLESS_GLYPH && (s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE || s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM)) height = ascent = s->first_glyph->slice.glyphless.lower_yoff - s->first_glyph->slice.glyphless.upper_yoff; BView_SetHighColor (view, s->hl == DRAW_CURSOR ? FRAME_CURSOR_COLOR (s->f).pixel : face->background); BView_FillRectangle (view, x, y - ascent, s->width, height); s->background_filled_p = 1; } if (s->left_overhang && s->clip_head && !s->for_overlaps) { /* XXX: Why is this neccessary? */ BView_ClipToRect (view, s->clip_head->x, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); } if (s->hl == DRAW_CURSOR) BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg); else BView_SetHighColor (view, face->foreground); BView_MovePenTo (view, x, y); BView_SetFont (view, ((struct haikufont_info *) info)->be_font); if (from == to) { int len = CHAR_STRING (s->char2b[from], mb); BView_DrawString (view, (char *) mb, len); } else { ptrdiff_t b_len = 0; char *b = xmalloc (b_len); for (int idx = from; idx < to; ++idx) { int len = CHAR_STRING (s->char2b[idx], mb); b = xrealloc (b, b_len = (b_len + len)); if (len == 1) b[b_len - len] = mb[0]; else memcpy (b + b_len - len, mb, len); } BView_DrawString (view, b, b_len); xfree (b); } BView_EndClip (view); BView_draw_unlock (view); unblock_input (); return 1; } struct font_driver const haikufont_driver = { .type = LISPSYM_INITIALLY (Qhaiku), .case_sensitive = true, .get_cache = haikufont_get_cache, .list = haikufont_list, .match = haikufont_match, .draw = haikufont_draw, .open_font = haikufont_open, .close_font = haikufont_close, .prepare_face = haikufont_prepare_face, .encode_char = haikufont_encode_char, .text_extents = haikufont_text_extents, .shape = haikufont_shape }; void syms_of_haikufont (void) { DEFSYM (Qfontsize, "fontsize"); DEFSYM (Qfixed, "fixed"); DEFSYM (Qplain, "plain"); DEFSYM (Qultra_light, "ultra-light"); DEFSYM (Qthin, "thin"); DEFSYM (Qreverse_italic, "reverse-italic"); DEFSYM (Qreverse_oblique, "reverse-oblique"); DEFSYM (Qmonospace, "monospace"); DEFSYM (Qultra_condensed, "ultra-condensed"); DEFSYM (Qextra_condensed, "extra-condensed"); DEFSYM (Qcondensed, "condensed"); DEFSYM (Qsemi_condensed, "semi-condensed"); DEFSYM (Qsemi_expanded, "semi-expanded"); DEFSYM (Qexpanded, "expanded"); DEFSYM (Qextra_expanded, "extra-expanded"); DEFSYM (Qultra_expanded, "ultra-expanded"); DEFSYM (Qzh, "zh"); DEFSYM (Qko, "ko"); DEFSYM (Qjp, "jp"); font_cache = list (Qnil); staticpro (&font_cache); #ifdef HAVE_BE_FREETYPE #ifndef HAVE_HARFBUZZ Fput (Qhaiku, Qfont_driver_superseded_by, Qftbe); #else Fput (Qhaiku, Qfont_driver_superseded_by, Qftbehb); #endif #endif }