diff options
Diffstat (limited to 'src/sfntfont.c')
-rw-r--r-- | src/sfntfont.c | 4229 |
1 files changed, 4229 insertions, 0 deletions
diff --git a/src/sfntfont.c b/src/sfntfont.c new file mode 100644 index 00000000000..fb3feaeaf79 --- /dev/null +++ b/src/sfntfont.c @@ -0,0 +1,4229 @@ +/* sfnt format font driver for GNU Emacs. + +Copyright (C) 2023-2024 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 <fcntl.h> +#include <ctype.h> + +#include "lisp.h" + +#include "blockinput.h" +#include "charset.h" +#include "coding.h" +#include "font.h" +#include "frame.h" +#include "math.h" +#include "sfnt.h" +#include "sfntfont.h" + +#ifdef HAVE_HARFBUZZ +#include <hb.h> +#include <hb-ot.h> +#endif /* HAVE_HARFBUZZ */ + +/* For FRAME_FONT. */ +#include TERM_HEADER + +/* Generic font driver for sfnt-based fonts (currently TrueType, but + it would be easy to add CFF support in the future with a PostScript + renderer.) + + This is not a complete font driver. Hooks must be supplied by the + platform implementer to draw glyphs. */ + + + +/* Tables associated with each font, be it distortable or not. This + allows different font objects sharing the same underlying font file + to share tables. */ + +struct sfnt_font_tables +{ + /* Various tables required to use the font. */ + struct sfnt_cmap_table *cmap; + struct sfnt_hhea_table *hhea; + struct sfnt_maxp_table *maxp; + struct sfnt_head_table *head; + struct sfnt_hmtx_table *hmtx; + struct sfnt_glyf_table *glyf; + struct sfnt_loca_table_short *loca_short; + struct sfnt_loca_table_long *loca_long; + struct sfnt_prep_table *prep; + struct sfnt_fpgm_table *fpgm; + struct sfnt_cvt_table *cvt; + struct sfnt_fvar_table *fvar; + struct sfnt_avar_table *avar; + struct sfnt_gvar_table *gvar; + struct sfnt_cvar_table *cvar; + + /* The selected character map. */ + struct sfnt_cmap_encoding_subtable_data *cmap_data; + + /* Data identifying that character map. */ + struct sfnt_cmap_encoding_subtable cmap_subtable; + + /* The UVS context. */ + struct sfnt_uvs_context *uvs; + +#ifdef HAVE_MMAP + /* Whether or not the glyph table has been mmapped. */ + bool glyf_table_mapped; +#endif /* HAVE_MMAP */ + +#ifdef HAVE_HARFBUZZ + /* File descriptor associated with this font. */ + int fd; + + /* The table directory of the font file. */ + struct sfnt_offset_subtable *directory; +#endif /* HAVE_HARFBUZZ */ +}; + +/* Description of a font that hasn't been opened. */ + +struct sfnt_font_desc +{ + /* Next font in this list. */ + struct sfnt_font_desc *next; + + /* Family name of the font. */ + Lisp_Object family; + + /* Style name of the font. */ + Lisp_Object style; + + /* The font foundry name, or `misc' if not present. */ + Lisp_Object designer; + + /* Style tokens that could not be parsed. */ + Lisp_Object adstyle; + + /* List of design languages. */ + Lisp_Object languages; + + /* Font registry that this font supports. */ + Lisp_Object registry; + + /* Vector of instances. Each element is another of the instance's + `style', `adstyle', and numeric width, weight, and slant. May be + nil. */ + Lisp_Object instances; + + /* Numeric width, weight, slant and spacing. */ + int width, weight, slant, spacing; + + /* Path to the font file. */ + char *path; + + /* char table consisting of characters already known to be + present in the font. */ + Lisp_Object char_cache; + + /* The header of the cmap being used. May be invalid, in which case + platform_id will be 500. */ + struct sfnt_cmap_encoding_subtable subtable; + + /* The offset of the table directory within PATH. */ + off_t offset; + + /* List of font tables. */ + struct sfnt_font_tables *tables; + + /* The number of glyphs in this font. Used to catch invalid cmap + tables. This is actually the number of glyphs - 1. */ + int num_glyphs; + + /* The number of references to the font tables below. */ + int refcount; + + /* The underline position and thickness if a post table supplies + this information. */ + sfnt_fword underline_position, underline_thickness; + + /* Whether an underline position is available. */ + bool_bf underline_position_set : 1; + + /* Whether or not the character map can't be used by Emacs. */ + bool cmap_invalid : 1; +}; + +/* List of fonts. */ + +static struct sfnt_font_desc *system_fonts; + +/* Font enumeration and matching. The sfnt driver assumes it can read + data from each font at startup. It then reads the head, meta and + name tables to determine font data, and records the font in a list + of system fonts that is then matched against. */ + +/* Set up the coding system CODING to decode string data from the + given platform id ID and platform specific id PLATFORM_SPECIFIC_ID. + Value is 0 upon success, 1 upon failure. */ + +static int +sfnt_setup_coding_system (enum sfnt_platform_id id, int platform_specific_id, + struct coding_system *coding) +{ + Lisp_Object system; + + system = Qnil; + + /* Figure out what coding system to use. */ + + switch (id) + { + case SFNT_PLATFORM_UNICODE: + system = Qutf_16be; + break; + + case SFNT_PLATFORM_MACINTOSH: + + if (platform_specific_id == SFNT_MACINTOSH_ROMAN) + system = Qmac_roman; + else + /* MULE doesn't support the rest... */ + system = Qnil; + + break; + + case SFNT_PLATFORM_MICROSOFT: + system = Qutf_16be; + + /* Not sure if this is right. */ + if (platform_specific_id == SFNT_MICROSOFT_BIG_FIVE) + system = Qchinese_big5; + + break; + + default: + system = Qnil; + } + + if (NILP (system)) + return 1; + + setup_coding_system (system, coding); + return 0; +} + +/* Globals used to communicate inside the condition-case wrapper. */ +static struct coding_system *sfnt_font_coding; + +/* The src_object being encoded from. This should be on the stack as + well, or it will get garbage collected. */ +static Lisp_Object sfnt_font_src_object; + +/* From-position. */ +static ptrdiff_t sfnt_font_from, sfnt_font_from_byte; + +/* To-position. */ +static ptrdiff_t sfnt_font_to, sfnt_font_to_byte; + +/* Destination object. Once again, this should also be on the + stack. */ +static Lisp_Object sfnt_font_dst_object; + +/* Error flag. Set to true if a signal was caught. */ +static bool sfnt_font_signal; + +static Lisp_Object +sfnt_safe_decode_coding_object_1 (void) +{ + decode_coding_object (sfnt_font_coding, + sfnt_font_src_object, + sfnt_font_from, + sfnt_font_from_byte, + sfnt_font_to, + sfnt_font_to_byte, + sfnt_font_dst_object); + return Qnil; +} + +static Lisp_Object +sfnt_safe_decode_coding_object_2 (Lisp_Object error) +{ + sfnt_font_signal = true; + + return Qnil; +} + +/* Like decode_coding_object, but return 1 if a signal happens. Value + is otherwise 0. */ + +static int +sfnt_safe_decode_coding_object (struct coding_system *coding, + Lisp_Object src_object, + ptrdiff_t from, ptrdiff_t from_byte, + ptrdiff_t to, ptrdiff_t to_byte, + Lisp_Object dst_object) +{ + sfnt_font_coding = coding; + sfnt_font_src_object = src_object; + sfnt_font_from = from; + sfnt_font_from_byte = from_byte; + sfnt_font_to = to; + sfnt_font_to_byte = to_byte; + sfnt_font_dst_object = dst_object; + sfnt_font_signal = false; + + internal_condition_case (sfnt_safe_decode_coding_object_1, + Qt, + sfnt_safe_decode_coding_object_2); + + return (int) sfnt_font_signal; +} + +/* Decode the specified string DATA. The encoding is determined based + on PLATFORM_ID, PLATFORM_SPECIFIC_ID and LANGUAGE_ID. Consult + sfnt.h and the TrueType Reference Manual for more details. LENGTH + is the length of DATA in bytes. + + Value is nil upon failure, else the decoded string. */ + +static Lisp_Object +sfnt_decode_font_string (unsigned char *data, enum sfnt_platform_id id, + int platform_specific_id, int language_id, + size_t length) +{ + struct coding_system coding; + + memset (&coding, 0, sizeof coding); + sfnt_setup_coding_system (id, platform_specific_id, &coding); + coding.mode |= CODING_MODE_SAFE_ENCODING; + coding.mode |= CODING_MODE_LAST_BLOCK; + /* Suppress producing escape sequences for composition. */ + coding.common_flags &= ~CODING_ANNOTATION_MASK; + coding.source = data; + + if (sfnt_safe_decode_coding_object (&coding, Qnil, 0, 0, + length, length, Qt)) + return Qnil; + + return coding.dst_object; +} + +/* Decode the family and style names from the name table NAME. Return + 0 and the family and style names upon success, else 1. */ + +static int +sfnt_decode_family_style (struct sfnt_name_table *name, + Lisp_Object *family, Lisp_Object *style) +{ + struct sfnt_name_record family_rec, style_rec; + unsigned char *family_data, *style_data; + + /* Because MS-Windows is incapable of treating font families + comprising more than four styles correctly, the TrueType + specification incorporates additional PREFERRED_FAMILY and + PREFERRED_SUBFAMILY name resources that are meant to be consulted + over the traditional family and subfamily resources. When + present within fonts supplying unusual styles, these names hold + the ``actual'' typographic family and style of the font, in lieu + of the font family with the style affixed to the front and + Regular. */ + + family_data = sfnt_find_name (name, SFNT_NAME_PREFERRED_FAMILY, + &family_rec); + + if (!family_data) + family_data = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY, + &family_rec); + + style_data = sfnt_find_name (name, SFNT_NAME_PREFERRED_SUBFAMILY, + &style_rec); + + if (!style_data) + style_data = sfnt_find_name (name, SFNT_NAME_FONT_SUBFAMILY, + &style_rec); + + if (!family_data || !style_data) + return 1; + + /* Now decode the data. */ + *family = sfnt_decode_font_string (family_data, + family_rec.platform_id, + family_rec.platform_specific_id, + family_rec.language_id, + family_rec.length); + *style = sfnt_decode_font_string (style_data, + style_rec.platform_id, + style_rec.platform_specific_id, + style_rec.language_id, + style_rec.length); + + /* Return whether or not it was successful. */ + return (!NILP (*family) && !NILP (*style)) ? 0 : 1; +} + +/* Decode the name of the specified font INSTANCE using the given NAME + table. Return the name of that instance, or nil upon failure. */ + +static Lisp_Object +sfnt_decode_instance_name (struct sfnt_instance *instance, + struct sfnt_name_table *name) +{ + struct sfnt_name_record name_rec; + unsigned char *name_data; + + name_data = sfnt_find_name (name, instance->name_id, + &name_rec); + + if (!name_data) + return Qnil; + + return sfnt_decode_font_string (name_data, + name_rec.platform_id, + name_rec.platform_specific_id, + name_rec.language_id, + name_rec.length); +} + +struct sfnt_style_desc +{ + /* The C string to match against. */ + const char *c_string; + + /* The value of the style field. */ + int value; +}; + +/* Array of style descriptions describing weight. */ +static struct sfnt_style_desc sfnt_weight_descriptions[] = + { + { "thin", 0, }, + { "extralight", 40, }, + { "ultralight", 40, }, + { "light", 50, }, + { "demilight", 55, }, + { "semilight", 55, }, + { "book", 75, }, + { "medium", 100, }, + { "demibold", 180, }, + { "semibold", 180, }, + { "bold", 200, }, + { "extrabold", 205, }, + { "ultrabold", 205, }, + { "black", 210, }, + { "heavy", 210, }, + { "extrablack", 215, }, + { "ultrablack", 215, }, + }; + +/* Array of style descriptions describing slant. */ +static struct sfnt_style_desc sfnt_slant_descriptions[] = + { + { "italic", 200, }, + { "oblique", 210, }, + }; + +/* Array of style descriptions describing width. */ +static struct sfnt_style_desc sfnt_width_descriptions[] = + { + { "ultracondensed", 50, }, + { "extracondensed", 63, }, + { "condensed", 75, }, + { "semicondensed", 87, }, + { "semiexpanded", 113, }, + { "expanded", 125, }, + { "extraexpanded", 150, }, + { "ultraexpanded", 200, }, + }; + +/* Figure out DESC->width, DESC->weight, DESC->slant and DESC->spacing + based on the style name passed as STYLE_NAME. + + Also append any unknown tokens to DESC->adstyle. */ + +static void +sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) +{ + char *style, *single, *saveptr; + int i; + USE_SAFE_ALLOCA; + + /* Fill in default values. slant seems to not be consistent with + Fontconfig. */ + desc->weight = 80; + desc->slant = 100; + desc->width = 100; + + /* Split the style into tokens delimited by spaces. Attempt to find + a token specifying each of the weight, slant, or width attributes + using their respective descriptions arrays as a reference. */ + + SAFE_ALLOCA_STRING (style, Fdowncase (style_name)); + saveptr = NULL; + + while ((single = strtok_r (style, " ", &saveptr))) + { + style = NULL; + + if (!strcmp (single, "regular")) + /* ``Regular'' within a font family can represent either the + weight, slant or width of the font. Leave each value as + its default, but never append it to the adstyle. */ + goto next; + + if (desc->weight == 80) + { + /* Weight hasn't been found yet. Scan through the weight + table. */ + for (i = 0; i < ARRAYELTS (sfnt_weight_descriptions); ++i) + { + if (!strcmp (sfnt_weight_descriptions[i].c_string, + single)) + { + /* Weight found. Continue on reading the slant and + width. */ + desc->weight = sfnt_weight_descriptions[i].value; + goto next; + } + } + } + + if (desc->slant == 100) + { + /* Slant hasn't been found yet. Scan through the slant + table. */ + for (i = 0; i < ARRAYELTS (sfnt_slant_descriptions); ++i) + { + if (!strcmp (sfnt_slant_descriptions[i].c_string, + single)) + { + /* Slant found. Continue on reading the weight and + width. */ + desc->slant = sfnt_slant_descriptions[i].value; + goto next; + } + } + } + + if (desc->width == 100) + { + /* Width hasn't been found yet. Scan through the width + table. */ + for (i = 0; i < ARRAYELTS (sfnt_width_descriptions); ++i) + { + if (!strcmp (sfnt_width_descriptions[i].c_string, + single)) + { + /* Width found. Continue on reading the slant and + weight. */ + desc->width = sfnt_width_descriptions[i].value; + goto next; + } + } + } + + /* This token is extraneous or was not recognized. Capitalize + the first letter and set it as the adstyle. */ + + if (strlen (single)) + { + if (islower (single[0])) + single[0] = toupper (single[0]); + + if (NILP (desc->adstyle)) + desc->adstyle = build_string (single); + else + desc->adstyle = CALLN (Fconcat, desc->adstyle, + build_string (" "), + build_string (single)); + } + + next: + continue; + } + + /* The adstyle must be a symbol, so intern it if it is set. */ + + if (!NILP (desc->adstyle)) + desc->adstyle = Fintern (desc->adstyle, Qnil); + + SAFE_FREE (); +} + +/* Parse the list of design languages in META, a font metadata table, + and place the results in DESC->languages. Do nothing if there is + no such metadata. */ + +static void +sfnt_parse_languages (struct sfnt_meta_table *meta, + struct sfnt_font_desc *desc) +{ + char *data, *metadata, *tag; + struct sfnt_meta_data_map map; + char *saveptr; + + /* Look up the ``design languages'' metadata. This is a comma (and + possibly space) separated list of scripts that the font was + designed for. Here is an example of one such tag: + + zh-Hans,Jpan,Kore + + for a font that covers Simplified Chinese, along with Japanese + and Korean text. */ + + saveptr = NULL; + data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_DLNG, + &map); + + if (!data) + { + /* Fall back to the supported languages metadata. */ + data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_SLNG, + &map); + + if (!data) + return; + } + + USE_SAFE_ALLOCA; + + /* Now copy metadata and add a trailing NULL byte. */ + + if (map.data_length >= SIZE_MAX) + memory_full (SIZE_MAX); + + metadata = SAFE_ALLOCA ((size_t) map.data_length + 1); + memcpy (metadata, data, map.data_length); + metadata[map.data_length] = '\0'; + + /* Loop through each script-language tag. Note that there may be + extra leading spaces. */ + while ((tag = strtok_r (metadata, ",", &saveptr))) + { + metadata = NULL; + + if (strstr (tag, "Hans") || strstr (tag, "Hant")) + desc->languages = Fcons (Qzh, desc->languages); + + if (strstr (tag, "Japn")) + desc->languages = Fcons (Qja, desc->languages); + + if (strstr (tag, "Kore")) + desc->languages = Fcons (Qko, desc->languages); + } + + SAFE_FREE (); +} + +/* Return the font registry corresponding to the encoding subtable + SUBTABLE. + + Under X, the font registry is an atom registered with the Open + Group uniquely identifying the organization which defines the + font's character set. + + In practice, the registry overlaps with the character set itself. + So Emacs just uses the ``registry'' field of each font object and + entity to represent both instead. */ + +static Lisp_Object +sfnt_registry_for_subtable (struct sfnt_cmap_encoding_subtable *subtable) +{ + switch (subtable->platform_id) + { + case SFNT_PLATFORM_UNICODE: + /* Reject variation selector and last resort tables. */ + if ((subtable->platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + || (subtable->platform_specific_id + == SFNT_UNICODE_LAST_RESORT)) + return Qnil; + + return Qiso10646_1; + + case SFNT_PLATFORM_MACINTOSH: + + switch (subtable->platform_specific_id) + { + case SFNT_MACINTOSH_ROMAN: + /* X calls mac-roman ``apple-roman''. */ + return Qapple_roman; + + default: + /* Some other Macintosh charset not supported by Emacs. */ + return Qnil; + } + + case SFNT_PLATFORM_MICROSOFT: + + /* Microsoft specific encodings. */ + + switch (subtable->platform_specific_id) + { + case SFNT_MICROSOFT_SYMBOL: + case SFNT_MICROSOFT_UNICODE_BMP: + /* Symbols in the Unicode PUA are still Unicode. */ + return Qiso10646_1; + + case SFNT_MICROSOFT_SHIFT_JIS: + return Qjisx0208_1983_0; + + case SFNT_MICROSOFT_PRC: + return Qgbk; + + case SFNT_MICROSOFT_JOHAB: + return Qksc5601_1987_0; + + case SFNT_MICROSOFT_UNICODE_UCS_4: + return Qiso10646_1; + } + + default: + return Qnil; + } +} + +/* Return the type of characters that the cmap subtable SUBTABLE maps + from. Value is: + + 2 if SUBTABLE maps from Unicode characters, including those outside + the Unicode Basic Multilingual Plane (BMP). + + 1 if SUBTABLE maps from Unicode characters within the BMP. + + 0 if SUBTABLE maps from some other character set that Emacs knows + about. + + 3 if SUBTABLE cannot be used by Emacs. */ + +static int +sfntfont_identify_cmap (struct sfnt_cmap_encoding_subtable subtable) +{ + switch (subtable.platform_id) + { + case SFNT_PLATFORM_UNICODE: + + /* Reject variation selector and last resort tables. */ + if ((subtable.platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + || (subtable.platform_specific_id + == SFNT_UNICODE_LAST_RESORT)) + return 3; + + /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within + the BMP. */ + if (subtable.platform_specific_id < SFNT_UNICODE_2_0) + return 1; + + return 2; + + case SFNT_PLATFORM_MACINTOSH: + + switch (subtable.platform_specific_id) + { + case SFNT_MACINTOSH_ROMAN: + /* mac-roman */ + return 0; + + default: + /* Some other Macintosh charset not supported by Emacs. */ + return 3; + } + + case SFNT_PLATFORM_MICROSOFT: + + /* Microsoft specific encodings. */ + + switch (subtable.platform_specific_id) + { + case SFNT_MICROSOFT_SYMBOL: + /* Symbols in the Unicode PUA are still Unicode. */ + return 1; + + case SFNT_MICROSOFT_UNICODE_BMP: + return 1; + + case SFNT_MICROSOFT_SHIFT_JIS: + /* PCK aka japanese-jisx0208. */ + return 0; + + case SFNT_MICROSOFT_PRC: + /* GBK, GB2312 or GB18030. */ + return 0; + + case SFNT_MICROSOFT_JOHAB: + /* KS C 5601-1992, aka korean-ksc5601. */ + return 0; + + case SFNT_MICROSOFT_UNICODE_UCS_4: + /* Unicode past the BMP. */ + return 2; + } + + default: + return 3; + } +} + +/* Figure out which registry DESC, backed by FD, whose table directory + is SUBTABLE, is likely to support. + + Read the header of each subtable in the character map and compute + the registry to use; then, set DESC->registry to that value. */ + +static void +sfnt_grok_registry (int fd, struct sfnt_font_desc *desc, + struct sfnt_offset_subtable *subtable) +{ + struct sfnt_cmap_table *cmap; + struct sfnt_cmap_encoding_subtable *subtables; + int i; + + cmap = sfnt_read_cmap_table (fd, subtable, &subtables, NULL); + + if (!cmap) + return; + + /* Now pick the ``best'' character map the same way as sfntfont_open + does. The caveat is that since the subtable data has not been + read, Emacs cannot determine whether or not the encoding subtable + is valid. + + Once platform_id is set, that value becomes much more + reliable. */ + + /* First look for a non-BMP Unicode cmap. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (sfntfont_identify_cmap (subtables[i]) == 2) + { + desc->registry + = sfnt_registry_for_subtable (&subtables[i]); + goto done; + } + } + + /* Next, look for a BMP only Unicode cmap. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (sfntfont_identify_cmap (subtables[i]) == 1) + { + desc->registry + = sfnt_registry_for_subtable (&subtables[i]); + goto done; + } + } + + /* Finally, use the first cmap that appears and can be + identified. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (sfntfont_identify_cmap (subtables[i]) == 0) + { + desc->registry + = sfnt_registry_for_subtable (&subtables[i]); + goto done; + } + } + + /* There are no cmaps available to Emacs. */ + done: + xfree (cmap); + xfree (subtables); +} + +/* Return whether or not the font description PREV conflicts with the + newer font description DESC, and should be removed from the list of + system fonts. + + If both PREV and DESC are variable fonts, remove styles within PREV + that overlap with DESC and return false. + + If PREV is a variable font, potentially adjust its list of + instances. */ + +static bool +sfnt_replace_fonts_p (struct sfnt_font_desc *prev, + struct sfnt_font_desc *desc) +{ + int i, j, width, weight, slant, count_instance; + Lisp_Object tem, tem1; + bool family_equal_p; + + family_equal_p = !NILP (Fstring_equal (prev->family, + desc->family)); + + if ((!NILP (desc->instances) + || !NILP (Fstring_equal (prev->style, desc->style))) + && family_equal_p) + { + /* If both inputs are GX fonts... */ + if (!NILP (desc->instances) && !NILP (prev->instances)) + { + /* ...iterate over each of the styles provided by PREV. If + they match any styles within DESC, remove the old style + from PREV. */ + + count_instance = 0; + for (i = 0; i < ASIZE (prev->instances); ++i) + { + tem = AREF (prev->instances, i); + + if (NILP (tem)) + continue; + + for (j = 0; j < ASIZE (desc->instances); ++j) + { + tem1 = AREF (desc->instances, j); + + if (NILP (tem1)) + continue; + + if (!NILP (Fequal (tem1, tem))) + { + /* tem1 is identical to tem, so opt for it over + tem. */ + ASET (prev->instances, i, Qnil); + goto next; + } + } + + /* Increment the number of instances remaining within + PREV. */ + count_instance++; + + next: + ; + } + + /* Return true if no instances remain inside + PREV->instances, so that the now purposeless desc may be + removed. */ + return !count_instance; + } + + return true; + } + + if (NILP (prev->instances) || !family_equal_p) + return false; + + /* Look through instances in PREV to see if DESC provides the same + thing. */ + + count_instance = 0; + for (i = 0; i < ASIZE (prev->instances); ++i) + { + tem = AREF (prev->instances, i); + + if (NILP (tem)) + continue; + + width = XFIXNUM (AREF (tem, 2)); + weight = XFIXNUM (AREF (tem, 3)); + slant = XFIXNUM (AREF (tem, 4)); + + if (desc->width == width + && desc->weight == weight + && desc->slant == slant) + { + /* Remove this instance. */ + ASET (prev->instances, i, Qnil); + continue; + } + + count_instance++; + } + + /* Remove this desc if there are no more instances. */ + return count_instance < 1; +} + +/* Enumerate the offset subtable SUBTABLES in the file FD, whose file + name is FILE. OFFSET should be the offset of the subtable within + the font file, and is recorded for future use. Value is 1 upon + failure, else 0. */ + +static int +sfnt_enum_font_1 (int fd, const char *file, + struct sfnt_offset_subtable *subtables, + off_t offset) +{ + struct sfnt_font_desc *desc, **next, *prev; + struct sfnt_head_table *head; + struct sfnt_name_table *name; + struct sfnt_meta_table *meta; + struct sfnt_maxp_table *maxp; + struct sfnt_fvar_table *fvar; + struct sfnt_OS_2_table *OS_2; + struct sfnt_post_table *post; + struct sfnt_font_desc temp; + Lisp_Object family, style, instance, style1; + int i; + char buffer[5]; + + /* Create the font desc and copy in the file name. */ + desc = xzalloc (sizeof *desc + strlen (file) + 1); + desc->path = (char *) (desc + 1); + memcpy (desc->path, file, strlen (file) + 1); + desc->offset = offset; + + /* Check that this is a TrueType font. */ + if (subtables->scaler_type != SFNT_SCALER_TRUE + && subtables->scaler_type != SFNT_SCALER_VER1) + goto bail1; + + /* Read required tables. */ + head = sfnt_read_head_table (fd, subtables); + if (!head) + goto bail1; + + name = sfnt_read_name_table (fd, subtables); + if (!name) + goto bail2; + + maxp = sfnt_read_maxp_table (fd, subtables); + if (!maxp) + goto bail3; + + /* meta is not required, nor present on many non-Apple fonts. */ + meta = sfnt_read_meta_table (fd, subtables); + + /* Decode the family and style from the name table. */ + if (sfnt_decode_family_style (name, &family, &style)) + goto bail4; + + /* See if this is a distortable/variable/multiple master font (all + three terms mean the same time.) */ + fvar = sfnt_read_fvar_table (fd, subtables); + + /* Set the family. */ + desc->family = family; + desc->char_cache = Qnil; + desc->subtable.platform_id = 500; + + /* Now set the font foundry name. This information is located + within the OS/2 table's `ach_vendor_id' field, but use `misc' as + a recourse if it is not present. */ + + OS_2 = sfnt_read_OS_2_table (fd, subtables); + + if (OS_2) + { + memcpy (buffer, OS_2->ach_vendor_id, + sizeof OS_2->ach_vendor_id); + buffer[sizeof OS_2->ach_vendor_id] = '\0'; + + /* If the foundry name is empty, use `misc' instead. */ + + if (!buffer[0]) + desc->designer = Qmisc; + else + desc->designer = intern (buffer); + + xfree (OS_2); + } + else + desc->designer = Qmisc; + + /* Set the largest glyph identifier. */ + desc->num_glyphs = maxp->num_glyphs; + + /* Parse the style. */ + sfnt_parse_style (style, desc); + + /* If the meta table exists, parse the list of design languages. */ + if (meta) + sfnt_parse_languages (meta, desc); + + /* Check whether the font claims to be a fixed pitch font and forgo + the rudimentary detection below if so. */ + + post = sfnt_read_post_table (fd, subtables); + + if (post) + { + desc->spacing = (post->is_fixed_pitch ? 100 : 0); + desc->underline_position = post->underline_position; + desc->underline_thickness = post->underline_thickness; + desc->underline_position_set = true; + xfree (post); + } + else + { + /* Figure out the spacing. Some fancy test like what Fontconfig + does is probably in order but not really necessary. */ + if (!NILP (Fstring_search (Fdowncase (family), + build_string ("mono"), + Qnil))) + desc->spacing = 100; /* FC_MONO */ + } + + /* Finally add mac-style flags. Allow them to override styles that + have not been found. */ + + if (head->mac_style & 01 && desc->weight == 80) /* Bold */ + desc->weight = 200; + + if (head->mac_style & 02 && desc->slant == 0) /* Italic */ + desc->slant = 100; + + /* Figure out what registry this font is likely to support. */ + sfnt_grok_registry (fd, desc, subtables); + + if (fvar && fvar->instance_count) + { + /* If there is an fvar table with instances, then this is a font + which defines different axes along which the points in each + glyph can be changed. + + Instead of enumerating the font itself, enumerate each + instance within, which specifies how to configure each axis + to achieve a specified style. */ + + desc->instances = make_vector (fvar->instance_count, Qnil); + + for (i = 0; i < fvar->instance_count; ++i) + { + style1 = sfnt_decode_instance_name (&fvar->instance[i], + name); + + if (NILP (style1)) + continue; + + /* Now parse the style. */ + temp.adstyle = Qnil; + sfnt_parse_style (style1, &temp); + + /* Set each field of the vector. */ + instance = make_vector (5, Qnil); + ASET (instance, 0, style1); + ASET (instance, 1, temp.adstyle); + ASET (instance, 2, make_fixnum (temp.width)); + ASET (instance, 3, make_fixnum (temp.weight)); + ASET (instance, 4, make_fixnum (temp.slant)); + + /* Place the vector in desc->instances. */ + ASET (desc->instances, i, instance); + } + } + + /* Set the style, link the desc onto system_fonts and return. */ + desc->style = style; + desc->next = system_fonts; + system_fonts = desc; + + /* Remove any fonts which have the same style as this one. For + distortable fonts, only remove overlapping styles, unless this is + also a distortable font. */ + + next = &system_fonts->next; + prev = *next; + for (; *next; prev = *next) + { + if (sfnt_replace_fonts_p (prev, desc)) + { + *next = prev->next; + xfree (prev); + } + else + next = &prev->next; + } + + xfree (fvar); + xfree (meta); + xfree (maxp); + xfree (name); + xfree (head); + return 0; + + bail4: + xfree (meta); + xfree (maxp); + bail3: + xfree (name); + bail2: + xfree (head); + bail1: + xfree (desc); + return 1; +} + +/* Enumerate the font FILE into the list of system fonts. Return 1 if + it could not be enumerated, 0 otherwise. + + Remove any font whose family and style is a duplicate of this one. + + FILE can either be a TrueType collection file containing TrueType + fonts, or a TrueType font itself. */ + +int +sfnt_enum_font (const char *file) +{ + int fd; + int rc; + off_t seek; + struct sfnt_offset_subtable *subtables; + struct sfnt_ttc_header *ttc; + size_t i; + + /* Now open the font for reading. */ + fd = emacs_open (file, O_RDONLY, 0); + + if (fd == -1) + goto bail; + + /* Read the table directory. */ + subtables = sfnt_read_table_directory (fd); + + if (subtables == (struct sfnt_offset_subtable *) -1) + { + /* This is actually a TrueType container file. Go back to the + beginning and read the TTC header. */ + + if (lseek (fd, 0, SEEK_SET)) + goto bail0; + + ttc = sfnt_read_ttc_header (fd); + + if (!ttc) + goto bail0; + + /* Enumerate each of the fonts in the collection. */ + + for (i = 0; i < ttc->num_fonts; ++i) + { + seek = lseek (fd, ttc->offset_table[i], SEEK_SET); + + if (seek == -1 || seek != ttc->offset_table[i]) + continue; + + subtables = sfnt_read_table_directory (fd); + + if (!subtables + /* This value means that FD was pointing at a TTC + header. Since FD should already have been moved to + the beginning of the TrueType header above, it + follows that the font format is invalid. */ + || (subtables == (struct sfnt_offset_subtable *) -1)) + continue; + + sfnt_enum_font_1 (fd, file, subtables, + ttc->offset_table[i]); + xfree (subtables); + } + + /* Always treat reading containers as having been + successful. */ + + emacs_close (fd); + xfree (ttc); + return 0; + } + + if (!subtables) + goto bail0; + + /* Now actually enumerate this font. */ + rc = sfnt_enum_font_1 (fd, file, subtables, 0); + xfree (subtables); + emacs_close (fd); + return rc; + + bail0: + emacs_close (fd); + bail: + return 1; +} + + + +/* Font discovery and matching. */ + +static struct charset * +sfntfont_charset_for_name (Lisp_Object symbol) +{ + ptrdiff_t idx; + int id; + + idx = CHARSET_SYMBOL_HASH_INDEX (symbol); + + if (idx == -1) + return NULL; + + /* Vcharset_hash_table is not a real variable, so Lisp programs + can't clobber it. */ + id = XFIXNUM (AREF (HASH_VALUE (XHASH_TABLE (Vcharset_hash_table), + idx), + charset_id)); + + return CHARSET_FROM_ID (id); +} + +/* Return the character set corresponding to a cmap subtable SUBTABLE. + Value is NULL if the subtable is not supported. */ + +static struct charset * +sfntfont_charset_for_cmap (struct sfnt_cmap_encoding_subtable subtable) +{ + switch (subtable.platform_id) + { + case SFNT_PLATFORM_UNICODE: + /* Reject variation selector and last resort tables. */ + if ((subtable.platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + || (subtable.platform_specific_id + == SFNT_UNICODE_LAST_RESORT)) + return NULL; + + /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within + the BMP. */ + if (subtable.platform_specific_id < SFNT_UNICODE_2_0) + return sfntfont_charset_for_name (Qunicode_bmp); + + return sfntfont_charset_for_name (Qunicode); + + case SFNT_PLATFORM_MACINTOSH: + + switch (subtable.platform_specific_id) + { + case SFNT_MACINTOSH_ROMAN: + return sfntfont_charset_for_name (Qmac_roman); + + default: + /* Some other Macintosh charset not supported by Emacs. */ + return NULL; + } + + case SFNT_PLATFORM_MICROSOFT: + + /* Microsoft specific encodings. */ + + switch (subtable.platform_specific_id) + { + case SFNT_MICROSOFT_SYMBOL: + /* Symbols in the Unicode PUA are still Unicode. */ + return sfntfont_charset_for_name (Qunicode); + + case SFNT_MICROSOFT_UNICODE_BMP: + return sfntfont_charset_for_name (Qunicode_bmp); + + case SFNT_MICROSOFT_SHIFT_JIS: + /* PCK aka japanese-jisx0208. */ + return sfntfont_charset_for_name (Qjapanese_jisx0208); + + case SFNT_MICROSOFT_PRC: + /* GBK, GB2312 or GB18030. */ + return sfntfont_charset_for_name (Qgbk); + + case SFNT_MICROSOFT_JOHAB: + /* KS C 5601-1992, aka korean-ksc5601. */ + return sfntfont_charset_for_name (Qkorean_ksc5601); + + case SFNT_MICROSOFT_UNICODE_UCS_4: + /* Unicode past the BMP. */ + return sfntfont_charset_for_name (Qucs); + } + + default: + return NULL; + } +} + +/* Pick the best character map in the cmap table CMAP. Use the + subtables in SUBTABLES and DATA. Return the subtable data and the + subtable in *SUBTABLE upon success, NULL otherwise. + + If FORMAT14 is non-NULL, return any associated format 14 variation + selection context in *FORMAT14 should the selected character map be + a Unicode character map. */ + +static struct sfnt_cmap_encoding_subtable_data * +sfntfont_select_cmap (struct sfnt_cmap_table *cmap, + struct sfnt_cmap_encoding_subtable *subtables, + struct sfnt_cmap_encoding_subtable_data **data, + struct sfnt_cmap_encoding_subtable *subtable, + struct sfnt_cmap_format_14 **format14) +{ + int i, j; + + /* First look for a non-BMP Unicode cmap. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (data[i] && sfntfont_identify_cmap (subtables[i]) == 2) + { + *subtable = subtables[i]; + + if (!format14) + return data[i]; + + /* Search for a corresponding format 14 character map. + This is used in conjunction with the selected character + map to map variation sequences. */ + + for (j = 0; j < cmap->num_subtables; ++j) + { + if (data[j] + && subtables[j].platform_id == SFNT_PLATFORM_UNICODE + && (subtables[j].platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + && data[j]->format == 14) + *format14 = (struct sfnt_cmap_format_14 *) data[j]; + } + + return data[i]; + } + } + + /* Next, look for a BMP only Unicode cmap. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (data[i] && sfntfont_identify_cmap (subtables[i]) == 1) + { + *subtable = subtables[i]; + + if (!format14) + return data[i]; + + /* Search for a corresponding format 14 character map. + This is used in conjunction with the selected character + map to map variation sequences. */ + + for (j = 0; j < cmap->num_subtables; ++j) + { + if (data[j] + && subtables[j].platform_id == SFNT_PLATFORM_UNICODE + && (subtables[j].platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + && data[j]->format == 14) + *format14 = (struct sfnt_cmap_format_14 *) data[j]; + } + + return data[i]; + } + } + + /* Finally, use the first cmap that appears and can be + identified. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (data[i] && sfntfont_identify_cmap (subtables[i]) == 0) + { + *subtable = subtables[i]; + return data[i]; + } + } + + /* There are no cmaps available to Emacs. */ + return NULL; +} + +/* Read the cmap from the font descriptor DESC, and place it in CMAP. + Keep *CMAP untouched if opening the cmap fails. Set SUBTABLE to + the cmap's header upon success. */ + +static void +sfntfont_read_cmap (struct sfnt_font_desc *desc, + struct sfnt_cmap_encoding_subtable_data **cmap, + struct sfnt_cmap_encoding_subtable *subtable) +{ + struct sfnt_offset_subtable *font; + struct sfnt_cmap_encoding_subtable *subtables; + struct sfnt_cmap_encoding_subtable_data **data; + struct sfnt_cmap_table *table; + int fd, i; + + /* Pick a character map and place it in *CMAP. */ + fd = emacs_open (desc->path, O_RDONLY, 0); + + if (fd < 0) + return; + + /* Seek to the start of the font itself within its collection. */ + + if (desc->offset + && lseek (fd, desc->offset, SEEK_SET) != desc->offset) + { + emacs_close (fd); + return; + } + + font = sfnt_read_table_directory (fd); + + /* Return if FONT is a TrueType collection: the file pointer should + already have been moved to the start of the table directory if + so. */ + + if (!font || (font == (struct sfnt_offset_subtable *) -1)) + { + emacs_close (fd); + return; + } + + table = sfnt_read_cmap_table (fd, font, &subtables, + &data); + xfree (font); + + if (!table) + { + emacs_close (fd); + return; + } + + /* Now pick the best character map. */ + + *cmap = sfntfont_select_cmap (table, subtables, data, + subtable, NULL); + + /* Free the cmap data. */ + + for (i = 0; i < table->num_subtables; ++i) + { + if (data[i] != *cmap) + xfree (data[i]); + } + + xfree (data); + xfree (subtables); + xfree (table); + emacs_close (fd); +} + +/* Return whether or not CHARACTER has an associated mapping in CMAP, + and the mapping points to a valid glyph. DESC is the font + descriptor associated with the font. */ + +static bool +sfntfont_glyph_valid (struct sfnt_font_desc *desc, + sfnt_char font_character, + struct sfnt_cmap_encoding_subtable_data *cmap) +{ + sfnt_glyph glyph; + + glyph = sfnt_lookup_glyph (font_character, cmap); + + if (!glyph) + return false; + + return glyph <= desc->num_glyphs; +} + +/* Look up a character CHARACTER in the font description DESC. Cache + the results. Return true if the character exists, false otherwise. + + If *CMAP is NULL, select a character map for the font and save it + there. Otherwise, use the character map in *CMAP. Save data + associated with the character map in *SUBTABLE. */ + +static bool +sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character, + struct sfnt_cmap_encoding_subtable_data **cmap, + struct sfnt_cmap_encoding_subtable *subtable) +{ + Lisp_Object cached; + sfnt_char font_character; + struct charset *charset; + bool present; + + /* Return false for characters that don't fit in a char table. */ + if (XFIXNUM (character) > INT_MAX || XFIXNUM (character) < 0) + return false; + + if (!NILP (desc->char_cache)) + { + cached = char_table_ref (desc->char_cache, + XFIXNUM (character)); + if (!NILP (cached)) + return (EQ (cached, Qlambda) ? false : true); + } + + if (!*cmap && !desc->cmap_invalid) + sfntfont_read_cmap (desc, cmap, subtable); + + /* Check that a cmap is now present. */ + if (!*cmap) + { + /* Opening the cmap failed. Set desc->cmap_invalid to avoid + opening it again. */ + desc->cmap_invalid = true; + return false; + } + + /* Otherwise, encode the character. */ + + charset = sfntfont_charset_for_cmap (*subtable); + if (!charset) + /* Emacs missing charsets? */ + return false; + + font_character = ENCODE_CHAR (charset, (int) XFIXNUM (character)); + + if (font_character == CHARSET_INVALID_CODE (charset)) + return false; + + /* Now return whether or not the glyph is present. Noto Sans + Georgian comes with a corrupt format 4 cmap table that somehow + tries to express glyphs greater than 65565. */ + present = sfntfont_glyph_valid (desc, font_character, *cmap); + + /* Cache the result. Store Qlambda when not present, Qt + otherwise. */ + + if (NILP (desc->char_cache)) + desc->char_cache = Fmake_char_table (Qfont_lookup_cache, + Qnil); + + Fset_char_table_range (desc->char_cache, character, + present ? Qt : Qlambda); + return present; +} + +/* Return whether or not the specified registry A is ``compatible'' + with registry B. + + Compatibility does not refer to whether or not the font registries + have an identical character set or repertory of characters. + + Instead, it refers to whether or not Emacs expects looking for A to + result in fonts used with B. */ + +static bool +sfntfont_registries_compatible_p (Lisp_Object a, Lisp_Object b) +{ + if (EQ (a, Qiso8859_1) && EQ (b, Qiso10646_1)) + return true; + + return EQ (a, b); +} + +/* Return whether or not the font description DESC satisfactorily + matches the font specification FONT_SPEC. + + Value is 0 if there is no match, -1 if there is a match against + DESC itself, and the number of matching instances if the style + matches one or more instances defined in in DESC. Return the index + of each matching instance in INSTANCES; it should be SIZE big. */ + +static int +sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec, + int *instances, int size) +{ + Lisp_Object tem, extra, tail; + struct sfnt_cmap_encoding_subtable_data *cmap; + size_t i; + struct sfnt_cmap_encoding_subtable subtable; + int instance, num_instance; + Lisp_Object item; + + /* cmap and subtable are caches for sfntfont_lookup_char. */ + + /* Check that the family name in SPEC matches DESC->family if it is + specified. */ + + tem = AREF (spec, FONT_FAMILY_INDEX); + + /* If TEM is a family listed in Vsfnt_default_family_alist, + then use that instead. */ + + if (SYMBOLP (tem) && CONSP (Vsfnt_default_family_alist)) + { + tail = Vsfnt_default_family_alist; + FOR_EACH_TAIL_SAFE (tail) + { + if (!CONSP (XCAR (tail))) + continue; + + if (STRINGP (XCAR (XCAR (tail))) + && STRINGP (XCDR (XCAR (tail))) + && !NILP (Fstring_equal (SYMBOL_NAME (tem), + XCAR (XCAR (tail))))) + { + /* Special family found. */ + tem = Fintern (XCDR (XCAR (tail)), Qnil); + break; + } + } + } + + if (!NILP (tem) && NILP (Fstring_equal (SYMBOL_NAME (tem), + desc->family))) + return 0; + + instance = -1; + + /* If a registry is set and wrong, then reject the font desc + immediately. This detects 50% of mismatches from fontset.c. + + If DESC->registry is nil, then the registry couldn't be + determined beforehand. */ + + tem = AREF (spec, FONT_REGISTRY_INDEX); + if (!NILP (tem) && !NILP (desc->registry) + && !sfntfont_registries_compatible_p (tem, desc->registry)) + return 0; + + /* If the font spacings disagree, reject this font also. */ + + tem = AREF (spec, FONT_SPACING_INDEX); + if (FIXNUMP (tem) && (XFIXNUM (tem) != desc->spacing)) + return 0; + + /* Check the style. If DESC is a fixed font, just check once. + Otherwise, check each instance. */ + + if (NILP (desc->instances)) + { + tem = AREF (spec, FONT_ADSTYLE_INDEX); + if (!NILP (tem) && !EQ (tem, desc->adstyle)) + return 0; + + if (FONT_WIDTH_NUMERIC (spec) != -1 + && FONT_WIDTH_NUMERIC (spec) != desc->width) + return 0; + + if (FONT_WEIGHT_NUMERIC (spec) != -1 + && FONT_WEIGHT_NUMERIC (spec) != desc->weight) + return 0; + + if (FONT_SLANT_NUMERIC (spec) != -1 + && FONT_SLANT_NUMERIC (spec) != desc->slant) + return 0; + } + else + { + num_instance = 0; + + /* Find the indices of instances in this distortable font which + match the given font spec. */ + + for (i = 0; i < ASIZE (desc->instances); ++i) + { + item = AREF (desc->instances, i); + + if (NILP (item)) + continue; + + /* Check that the adstyle specified matches. */ + + tem = AREF (spec, FONT_ADSTYLE_INDEX); + if (!NILP (tem) && NILP (Fequal (tem, AREF (item, 1)))) + continue; + + /* Check the style. */ + + if (FONT_WIDTH_NUMERIC (spec) != -1 + && (FONT_WIDTH_NUMERIC (spec) + != XFIXNUM (AREF (item, 2)))) + continue; + + if (FONT_WEIGHT_NUMERIC (spec) != -1 + && (FONT_WEIGHT_NUMERIC (spec) + != XFIXNUM (AREF (item, 3)))) + continue; + + if (FONT_SLANT_NUMERIC (spec) != -1 + && (FONT_SLANT_NUMERIC (spec) + != XFIXNUM (AREF (item, 4)))) + continue; + + if (num_instance == size) + break; + + /* A matching instance has been found. Set its index, then + go back to the rest of the font matching. */ + instances[num_instance++] = i; + } + + instance = num_instance; + } + + /* Handle extras. */ + extra = AREF (spec, FONT_EXTRA_INDEX); + + if (NILP (extra)) + return instance; + + tem = assq_no_quit (QCscript, extra); + cmap = NULL; + + if (!NILP (tem)) + { + /* If a script has been specified, look up its representative + characters and see if they are present in the font. This + requires reading the cmap. */ + tem = assq_no_quit (XCDR (tem), Vscript_representative_chars); + + if (CONSP (tem) && VECTORP (XCDR (tem))) + { + tem = XCDR (tem); + + /* The vector contains characters, of which one must be + present in the font. */ + for (i = 0; i < ASIZE (tem); ++i) + { + if (FIXNUMP (AREF (tem, i))) + { + if (!sfntfont_lookup_char (desc, AREF (tem, i), + &cmap, &subtable)) + goto fail; + + /* One character is enough to pass a font. Don't + look at too many. */ + break; + } + } + } + else if (CONSP (tem) && CONSP (XCDR (tem))) + { + tem = XCDR (tem); + + /* tem is a list of each characters, all of which must be + present in the font. */ + FOR_EACH_TAIL_SAFE (tem) + { + if (FIXNUMP (XCAR (tem)) + && !sfntfont_lookup_char (desc, XCAR (tem), &cmap, + &subtable)) + goto fail; + } + + /* One or more characters are missing. */ + if (!NILP (tem)) + goto fail; + } + /* Fail if there are no matching fonts at all. */ + else if (NILP (tem)) + goto fail; + } + + /* Now check that the language is supported. */ + tem = assq_no_quit (QClang, extra); + if (!NILP (tem) && NILP (Fmemq (tem, desc->languages))) + goto fail; + + /* Set desc->subtable if cmap was specified. */ + if (cmap) + desc->subtable = subtable; + + xfree (cmap); + return instance; + + fail: + /* The cmap might've been read in and require deallocation. */ + xfree (cmap); + return 0; +} + +/* Type of font entities and font objects created. */ +static Lisp_Object sfnt_vendor_name; + +/* Font driver used in font objects created. */ +static const struct font_driver *sfnt_font_driver; + +/* Return the font registry corresponding to the font descriptor DESC. + Under X, the font registry is an atom registered with the Open + Group uniquely identifying the organization which defines the + font's character set. + + In practice, the registry overlaps with the character set itself. + So Emacs just uses the ``registry'' field to represent both + instead. */ + +static Lisp_Object +sfntfont_registry_for_desc (struct sfnt_font_desc *desc) +{ + struct sfnt_cmap_encoding_subtable_data *cmap; + + cmap = NULL; + + if (desc->cmap_invalid) + return Qnil; + + if (desc->subtable.platform_id == 500) + { + /* Read in the cmap to determine the registry. */ + sfntfont_read_cmap (desc, &cmap, &desc->subtable); + + if (!cmap) + { + desc->cmap_invalid = true; + return Qnil; + } + } + + xfree (cmap); + + if (desc->subtable.platform_id != 500) + /* desc->subtable.platform_id is now set. CMAP is already free, + because it is not actually used. */ + return sfnt_registry_for_subtable (&desc->subtable); + + return Qnil; +} + +/* Return a font-entity that represents the font descriptor (unopened + font) DESC. If INSTANCE is more than or equal to 1, then it is the + index of the instance in DESC that should be opened plus 1; in that + case, DESC must be a distortable font. */ + +static Lisp_Object +sfntfont_desc_to_entity (struct sfnt_font_desc *desc, int instance) +{ + Lisp_Object entity, vector; + + entity = font_make_entity (); + + ASET (entity, FONT_TYPE_INDEX, sfnt_vendor_name); + ASET (entity, FONT_FOUNDRY_INDEX, desc->designer); + ASET (entity, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil)); + ASET (entity, FONT_ADSTYLE_INDEX, Qnil); + ASET (entity, FONT_REGISTRY_INDEX, + sfntfont_registry_for_desc (desc)); + + /* Size of 0 means the font is scalable. */ + ASET (entity, FONT_SIZE_INDEX, make_fixnum (0)); + ASET (entity, FONT_AVGWIDTH_INDEX, make_fixnum (0)); + ASET (entity, FONT_SPACING_INDEX, make_fixnum (desc->spacing)); + + if (instance >= 1) + { + if (NILP (desc->instances) + || instance > ASIZE (desc->instances)) + emacs_abort (); + + vector = AREF (desc->instances, instance - 1); + FONT_SET_STYLE (entity, FONT_WIDTH_INDEX, + AREF (vector, 2)); + FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX, + AREF (vector, 3)); + FONT_SET_STYLE (entity, FONT_SLANT_INDEX, + AREF (vector, 4)); + ASET (entity, FONT_ADSTYLE_INDEX, AREF (vector, 1)); + } + else + { + FONT_SET_STYLE (entity, FONT_WIDTH_INDEX, + make_fixnum (desc->width)); + FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX, + make_fixnum (desc->weight)); + FONT_SET_STYLE (entity, FONT_SLANT_INDEX, + make_fixnum (desc->slant)); + ASET (entity, FONT_ADSTYLE_INDEX, desc->adstyle); + } + + /* Set FONT_EXTRA_INDEX to a pointer to the font description. Font + descriptions are never supposed to be freed. */ + + ASET (entity, FONT_EXTRA_INDEX, + (instance >= 1 + ? list2 (Fcons (Qfont_entity, make_mint_ptr (desc)), + Fcons (Qfont_instance, make_fixnum (instance - 1))) + : list1 (Fcons (Qfont_entity, make_mint_ptr (desc))))); + + return entity; +} + +/* Return whether fewer fields inside the font entity A are set than + there are set inside the font entity B. */ + +static Lisp_Object +sfntfont_compare_font_entities (Lisp_Object a, Lisp_Object b) +{ + ptrdiff_t count_a, count_b, i; + + count_a = 0; + count_b = 0; + + for (i = 0; i < FONT_ENTITY_MAX; ++i) + { + if (!NILP (AREF (a, i))) + count_a++; + } + + for (i = 0; i < FONT_ENTITY_MAX; ++i) + { + if (!NILP (AREF (b, i))) + count_b++; + } + + return count_a < count_b ? Qt : Qnil; +} + +/* Function that compares two font entities to return whether fewer + fields are set within the first than in the second. */ + +static union Aligned_Lisp_Subr Scompare_font_entities = + { + { + { PSEUDOVECTOR_FLAG | (PVEC_SUBR << PSEUDOVECTOR_AREA_BITS), }, + { .a2 = sfntfont_compare_font_entities, }, + 2, 2, "sfntfont_compare_font_entities", {0}, lisp_h_Qnil, + }, + }; + +/* Return a list of font-entities matching the specified + FONT_SPEC. */ + +Lisp_Object +sfntfont_list (struct frame *f, Lisp_Object font_spec) +{ + Lisp_Object matching, tem, compare_font_entities; + struct sfnt_font_desc *desc; + int i, rc, instances[100]; + + matching = Qnil; + + block_input (); + /* Returning irrelevant results on receiving an OTF form will cause + fontset.c to loop over and over, making displaying some + characters very slow. */ + tem = assq_no_quit (QCotf, AREF (font_spec, FONT_EXTRA_INDEX)); + if (CONSP (tem) && !NILP (XCDR (tem))) + { + unblock_input (); + return Qnil; + } + + /* Loop through known system fonts and add them one-by-one. */ + + for (desc = system_fonts; desc; desc = desc->next) + { + rc = sfntfont_list_1 (desc, font_spec, instances, + ARRAYELTS (instances)); + + if (rc < 0) + matching = Fcons (sfntfont_desc_to_entity (desc, 0), + matching); + else if (rc) + { + /* Add each matching instance. */ + + for (i = 0; i < rc; ++i) + matching = Fcons (sfntfont_desc_to_entity (desc, + instances[i] + 1), + matching); + } + } + unblock_input (); + + /* Sort matching by the number of fields set inside each element, so + that values of FONT_SPECs that leave a number of fields + unspecified will yield a list with the closest matches (that is + to say, those whose fields are precisely as specified by the + caller) ordered first. */ + + XSETSUBR (compare_font_entities, &Scompare_font_entities.s); + matching = CALLN (Fsort, matching, compare_font_entities); + return matching; +} + +/* Return the first font-entity matching the specified FONT_SPEC. */ + +Lisp_Object +sfntfont_match (struct frame *f, Lisp_Object font_spec) +{ + Lisp_Object matches; + + matches = sfntfont_list (f, font_spec); + + if (!NILP (matches)) + return XCAR (matches); + + return Qnil; +} + + + +enum + { + SFNT_OUTLINE_CACHE_SIZE = 256, + SFNT_RASTER_CACHE_SIZE = 128, + }; + +/* Caching subsystem. Generating outlines from glyphs is expensive, + and so is rasterizing them, so two caches are maintained for both + glyph outlines and rasters. + + Computing metrics also requires some expensive processing if the + glyph has instructions or distortions. */ + +struct sfnt_outline_cache +{ + /* Next and last cache buckets. */ + struct sfnt_outline_cache *next, *last; + + /* Pointer to outline. */ + struct sfnt_glyph_outline *outline; + + /* Reference to glyph metrics. */ + struct sfnt_glyph_metrics metrics; + + /* What glyph this caches. */ + sfnt_glyph glyph; +}; + +struct sfnt_raster_cache +{ + /* Next and last cache buckets. */ + struct sfnt_raster_cache *next, *last; + + /* Pointer to raster. */ + struct sfnt_raster *raster; + + /* What glyph this caches. */ + sfnt_glyph glyph; +}; + +struct sfntfont_get_glyph_outline_dcontext +{ + /* Long and short loca tables. */ + struct sfnt_loca_table_long *loca_long; + struct sfnt_loca_table_short *loca_short; + + /* glyf table. */ + struct sfnt_glyf_table *glyf; + + /* hmtx, hhea and maxp tables utilized to acquire glyph metrics. */ + struct sfnt_hmtx_table *hmtx; + struct sfnt_hhea_table *hhea; + struct sfnt_maxp_table *maxp; + + /* Variation settings, or NULL. */ + struct sfnt_blend *blend; +}; + +/* Return the glyph identified by GLYPH_ID from the glyf and loca + table specified in DCONTEXT. Set *NEED_FREE to true. */ + +static struct sfnt_glyph * +sfntfont_get_glyph (sfnt_glyph glyph_id, void *dcontext, + bool *need_free) +{ + struct sfntfont_get_glyph_outline_dcontext *tables; + struct sfnt_glyph *glyph; + struct sfnt_metrics_distortion distortion; + + tables = dcontext; + *need_free = true; + + glyph = sfnt_read_glyph (glyph_id, tables->glyf, + tables->loca_short, + tables->loca_long); + + if (tables->blend && glyph) + { + if (glyph->simple) + sfnt_vary_simple_glyph (tables->blend, glyph_id, glyph, + &distortion); + else + sfnt_vary_compound_glyph (tables->blend, glyph_id, glyph, + &distortion); + } + + /* Note that the distortion is not relevant for compound glyphs. */ + return glyph; +} + +/* Free the glyph identified by GLYPH. */ + +static void +sfntfont_free_glyph (struct sfnt_glyph *glyph, void *dcontext) +{ + sfnt_free_glyph (glyph); +} + +/* Return unscaled glyph metrics for the glyph designated by the ID + GLYPH within *METRICS, utilizing tables within DCONTEXT. + + Value is 1 upon failure, 0 otherwise. */ + +static int +sfntfont_get_metrics (sfnt_glyph glyph, struct sfnt_glyph_metrics *metrics, + void *dcontext) +{ + struct sfntfont_get_glyph_outline_dcontext *tables; + + tables = dcontext; + return sfnt_lookup_glyph_metrics (glyph, metrics, tables->hmtx, + tables->hhea, tables->maxp); +} + +/* Dereference the outline OUTLINE. Free it once refcount reaches + 0. */ + +static void +sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) +{ + eassert (outline->refcount > 0); + + if (--outline->refcount) + return; + + xfree (outline); +} + +/* Get the outline corresponding to the specified GLYPH_CODE in CACHE. + Use the scale factor SCALE, the glyf table GLYF, and the head table + HEAD. Keep *CACHE_SIZE updated with the number of elements in the + cache. + + Distort the glyph using BLEND if INDEX is not -1. + + Use the offset information in the long or short loca tables + LOCA_LONG and LOCA_SHORT, whichever is set. + + Use the specified HMTX, HEAD, HHEA and MAXP tables when instructing + compound glyphs. + + If INTERPRETER is non-NULL, then possibly use it and the + interpreter graphics STATE to instruct the glyph. + + If METRICS is non-NULL, return the scaled glyph metrics after + variation and instructing. + + Return the outline with an incremented reference count and enter + the generated outline into CACHE upon success, possibly discarding + any older outlines, or NULL on failure. */ + +static struct sfnt_glyph_outline * +sfntfont_get_glyph_outline (sfnt_glyph glyph_code, + struct sfnt_outline_cache *cache, + sfnt_fixed scale, int *cache_size, + struct sfnt_blend *blend, + int index, + struct sfnt_glyf_table *glyf, + struct sfnt_head_table *head, + struct sfnt_hmtx_table *hmtx, + struct sfnt_hhea_table *hhea, + struct sfnt_maxp_table *maxp, + struct sfnt_loca_table_short *loca_short, + struct sfnt_loca_table_long *loca_long, + struct sfnt_interpreter *interpreter, + struct sfnt_glyph_metrics *metrics, + struct sfnt_graphics_state *state) +{ + struct sfnt_outline_cache *start; + struct sfnt_glyph_outline *outline; + struct sfnt_glyph *glyph; + struct sfntfont_get_glyph_outline_dcontext dcontext; + struct sfnt_instructed_outline *value; + const char *error; + struct sfnt_glyph_metrics temp; + struct sfnt_metrics_distortion distortion; + sfnt_fixed advance; + + start = cache->next; + distortion.advance = 0; + + /* See if the outline is already cached. */ + for (; start != cache; start = start->next) + { + if (start->glyph == glyph_code) + { + /* Move start to the start of the ring. Then increase + start->outline->refcount and return it. */ + + start->last->next = start->next; + start->next->last = start->last; + + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + start->outline->refcount++; + + if (metrics) + *metrics = start->metrics; + + return start->outline; + } + } + + /* Not already cached. Get the glyph. */ + glyph = sfnt_read_glyph (glyph_code, glyf, + loca_short, loca_long); + + if (!glyph) + return NULL; + + /* Distort the glyph if necessary. */ + + if (index != -1) + { + if (glyph->simple) + { + if (sfnt_vary_simple_glyph (blend, glyph_code, + glyph, &distortion)) + { + sfnt_free_glyph (glyph); + return NULL; + } + } + else if (sfnt_vary_compound_glyph (blend, glyph_code, + glyph, &distortion)) + { + sfnt_free_glyph (glyph); + return NULL; + } + } + + /* Try to instruct the glyph if INTERPRETER is specified. */ + + outline = NULL; + + dcontext.loca_long = loca_long; + dcontext.loca_short = loca_short; + dcontext.glyf = glyf; + dcontext.hhea = hhea; + dcontext.hmtx = hmtx; + dcontext.maxp = maxp; + dcontext.blend = (index != -1 ? blend : NULL); + + /* Now load the glyph's unscaled metrics into TEMP. */ + + if (sfnt_lookup_glyph_metrics (glyph_code, &temp, hmtx, hhea, maxp)) + goto fail; + + if (interpreter) + { + if (glyph->simple) + { + /* Restore the interpreter state from the snapshot taken + after loading the preprogram. */ + interpreter->state = *state; + + error = sfnt_interpret_simple_glyph (glyph, interpreter, + &temp, &value); + } + else + /* Restoring the interpreter state is done by + sfnt_interpret_compound_glyph; all that must be done here + is to give the graphics state to that function. */ + error = sfnt_interpret_compound_glyph (glyph, interpreter, + state, + sfntfont_get_glyph, + sfntfont_free_glyph, + hmtx, hhea, maxp, + &temp, &dcontext, + &value); + + if (!error) + { + /* Now record the advance with that measured from the + phantom points within the instructed glyph outline, and + subsequently replace it once metrics are scaled. */ + + outline = sfnt_build_instructed_outline (value, + &advance); + xfree (value); + + if (outline) + { + /* Save the new advance width. This advance width is + rounded again, as the instruction code executed might + have moved both phantom points such that they no + longer measure a fractional distance. */ + temp.advance = SFNT_ROUND_FIXED (advance); + + /* Finally, adjust the left side bearing of the glyph + metrics by the origin point of the outline, should a + transformation have been applied by either + instruction code or glyph variation. The left side + bearing is the distance from the origin point to the + left most point on the X axis. */ + temp.lbearing + = SFNT_FLOOR_FIXED (outline->xmin - outline->origin); + } + } + } + + if (!outline) + { + /* Build the outline. This will apply GX offsets within *GLYPH + to TEMP. */ + outline = sfnt_build_glyph_outline (glyph, scale, + &temp, + sfntfont_get_glyph, + sfntfont_free_glyph, + sfntfont_get_metrics, + &dcontext); + + /* At this point, the glyph metrics are unscaled. Scale them + up. If INTERPRETER is set, use the scale placed within. */ + sfnt_scale_metrics (&temp, scale); + } + + fail: + + xfree (glyph); + + if (!outline) + return NULL; + + start = xmalloc (sizeof *start); + start->glyph = glyph_code; + start->outline = outline; + start->metrics = temp; + + /* One reference goes to the cache. The second reference goes to + the caller. */ + outline->refcount = 2; + + /* Link start onto the cache. */ + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + + /* Update the cache size. */ + (*cache_size)++; + + /* Figure out if the least recently used element has to be + evicted. */ + if (*cache_size > SFNT_OUTLINE_CACHE_SIZE) + { + start = cache->last; + eassert (start != cache); + + /* Free the least recently used entry in the cache. */ + start->last->next = start->next; + start->next->last = start->last; + sfntfont_dereference_outline (start->outline); + xfree (start); + + (*cache_size)--; + } + + /* Return the cached outline and metrics. */ + + if (metrics) + *metrics = temp; + + return outline; +} + +/* Free the outline cache referred to by CACHE. Dereference each + outline contained therein. */ + +static void +sfntfont_free_outline_cache (struct sfnt_outline_cache *cache) +{ + struct sfnt_outline_cache *next, *last; + + /* Handle partly initialized fonts. */ + if (!cache->next) + return; + + for (next = cache->next; next != cache;) + { + last = next; + next = next->next; + + sfntfont_dereference_outline (last->outline); + xfree (last); + } + + cache->next = cache; + cache->last = cache; +} + +/* Dereference the raster RASTER. Free it once refcount reaches + 0. */ + +static void +sfntfont_dereference_raster (struct sfnt_raster *raster) +{ + eassert (raster->refcount > 0); + + if (--raster->refcount) + return; + + xfree (raster); +} + +/* Get the raster corresponding to the specified GLYPH_CODE in CACHE. + Use the outline named OUTLINE. Keep *CACHE_SIZE updated with the + number of elements in the cache. */ + +static struct sfnt_raster * +sfntfont_get_glyph_raster (sfnt_glyph glyph_code, + struct sfnt_raster_cache *cache, + struct sfnt_glyph_outline *outline, + int *cache_size) +{ + struct sfnt_raster_cache *start; + struct sfnt_raster *raster; + + /* See if the raster is already cached. */ + start = cache->next; + + for (; start != cache; start = start->next) + { + if (start->glyph == glyph_code) + { + /* Move start to the start of the ring. Them, increase + start->raster->refcount and return it. */ + + start->last->next = start->next; + start->next->last = start->last; + + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + start->raster->refcount++; + + return start->raster; + } + } + + /* Not already cached. Raster the outline. */ + + if (!sfnt_raster_glyphs_exactly) + raster = sfnt_raster_glyph_outline (outline); + else + raster = sfnt_raster_glyph_outline_exact (outline); + + if (!raster) + return NULL; + + start = xmalloc (sizeof *start); + start->glyph = glyph_code; + start->raster = raster; + + /* One reference goes to the cache. The second reference goes to + the caller. */ + raster->refcount = 2; + + /* Link start onto the cache. */ + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + + /* Update the cache size. */ + (*cache_size)++; + + /* Figure out if the least recently used element has to be + evicted. */ + if (*cache_size > SFNT_OUTLINE_CACHE_SIZE) + { + start = cache->last; + eassert (start != cache); + + /* Free the least recently used entry in the cache. */ + start->last->next = start->next; + start->next->last = start->last; + sfntfont_dereference_raster (start->raster); + xfree (start); + + (*cache_size)--; + } + + /* Return the cached raster. */ + return raster; +} + +/* Free the raster cache referred to by CACHE. Dereference each + raster contained therein. */ + +static void +sfntfont_free_raster_cache (struct sfnt_raster_cache *cache) +{ + struct sfnt_raster_cache *next, *last; + + /* Handle partly initialized fonts. */ + if (!cache->next) + return; + + for (next = cache->next; next != cache;) + { + last = next; + next = next->next; + + sfntfont_dereference_raster (last->raster); + xfree (last); + } + + cache->next = cache; + cache->last = cache; +} + + + +/* Opening fonts. */ + +struct sfnt_font_info +{ + /* Parent font structure. */ + struct font font; + +#ifdef HAVE_MMAP + /* The next font in this chain. */ + struct sfnt_font_info *next; +#endif /* HAVE_MMAP */ + + /* The font description used to create this font. Used to + dereference tables associated with this font. */ + struct sfnt_font_desc *desc; + + /* Various tables required to use the font. */ + struct sfnt_cmap_table *cmap; + struct sfnt_hhea_table *hhea; + struct sfnt_maxp_table *maxp; + struct sfnt_head_table *head; + struct sfnt_hmtx_table *hmtx; + struct sfnt_glyf_table *glyf; + struct sfnt_loca_table_short *loca_short; + struct sfnt_loca_table_long *loca_long; + struct sfnt_prep_table *prep; + struct sfnt_fpgm_table *fpgm; + struct sfnt_cvt_table *cvt; + + /* The selected character map. */ + struct sfnt_cmap_encoding_subtable_data *cmap_data; + + /* Data identifying that character map. */ + struct sfnt_cmap_encoding_subtable cmap_subtable; + + /* The UVS context. */ + struct sfnt_uvs_context *uvs; + + /* Outline cache. */ + struct sfnt_outline_cache outline_cache; + + /* Number of elements in the outline cache. */ + int outline_cache_size; + + /* Raster cache. */ + struct sfnt_raster_cache raster_cache; + + /* Number of elements in the raster cache. */ + int raster_cache_size; + + /* Interpreter for grid fitting (if enabled). */ + struct sfnt_interpreter *interpreter; + + /* Graphics state after the execution of the font and control value + programs. */ + struct sfnt_graphics_state state; + + /* Factor used to convert from em space to pixel space. */ + sfnt_fixed scale; + + /* The blend (configuration of this multiple master font). */ + struct sfnt_blend blend; + + /* The index of the named instance used to initialize BLEND. + -1 if BLEND is not initialized. */ + int instance; + +#ifdef HAVE_MMAP + /* Whether or not the glyph table has been mmapped. */ + bool glyf_table_mapped; +#endif /* HAVE_MMAP */ + +#ifdef HAVE_HARFBUZZ + /* HarfBuzz font object. */ + hb_font_t *hb_font; + + /* File descriptor associated with this font. */ + int fd; + + /* The table directory of the font file. */ + struct sfnt_offset_subtable *directory; +#endif /* HAVE_HARFBUZZ */ +}; + +#ifdef HAVE_MMAP + +/* List of all open fonts. */ + +static struct sfnt_font_info *open_fonts; + +#endif /* HAVE_MMAP */ + +/* Look up the glyph corresponding to the character C in FONT. Return + 0 upon failure, and the glyph otherwise. */ + +static sfnt_glyph +sfntfont_lookup_glyph (struct sfnt_font_info *font_info, int c) +{ + struct charset *charset; + sfnt_char character; + sfnt_glyph glyph; + + charset = CHARSET_FROM_ID (font_info->font.encoding_charset); + + if (!charset) + return 0; + + character = ENCODE_CHAR (charset, c); + + if (character == CHARSET_INVALID_CODE (charset)) + return 0; + + /* Do the actual lookup with the encoded character. */ + glyph = sfnt_lookup_glyph (character, font_info->cmap_data); + + return glyph; +} + +static int sfntfont_measure_pcm (struct sfnt_font_info *, sfnt_glyph, + struct font_metrics *); + +/* Probe and set FONT_INFO->font.average_width, + FONT_INFO->font.space_width, and FONT_INFO->font.min_width + according to the tables contained therein. + + As this function generates outlines for all glyphs, outlines for + all ASCII characters will be entered into the outline cache as + well. */ + +static void +sfntfont_probe_widths (struct sfnt_font_info *font_info) +{ + int i, num_characters, total_width; + sfnt_glyph glyph; + struct font_metrics pcm; + + num_characters = 0; + total_width = 0; + + /* First set some reasonable default values. */ + font_info->font.average_width = font_info->font.pixel_size; + font_info->font.space_width = font_info->font.pixel_size; + font_info->font.min_width = 1; + + /* Next, loop through the common ASCII characters. Tally up their + advance widths and set space_width if necessary. */ + for (i = 32; i < 127; ++i) + { + glyph = sfntfont_lookup_glyph (font_info, i); + + if (!glyph) + continue; + + /* Now look up the metrics of this glyph. Data from the metrics + table doesn't fit the bill, since variations and instruction + code is not applied to it. */ + if (sfntfont_measure_pcm (font_info, glyph, &pcm)) + continue; + + /* Increase the number of characters. */ + num_characters++; + + /* Add the advance to total_width. */ + total_width += pcm.width; + + /* Update min_width if it hasn't been set yet or is wider. */ + if (font_info->font.min_width == 1 + || font_info->font.min_width > pcm.width) + font_info->font.min_width = pcm.width; + + /* If i is the space character, set the space width. Make sure + to round this up. */ + if (i == 32) + font_info->font.space_width = pcm.width; + } + + /* Now, if characters were found, set average_width. */ + if (num_characters) + font_info->font.average_width = total_width / num_characters; +} + +/* Initialize the instruction interpreter for INFO. Load the font and + preprogram for the pixel size in INFO and its corresponding point + size POINT_SIZE. Use the FVAR table in DESC. + + The font tables in INFO must already have been initialized. + + Set INFO->interpreter upon success, and leave that field intact + otherwise. */ + +static void +sfntfont_setup_interpreter (struct sfnt_font_info *info, + struct sfnt_font_desc *desc, + int point_size) +{ + struct sfnt_cvt_table *cvt; + struct sfnt_fpgm_table *fpgm; + struct sfnt_prep_table *prep; + struct sfnt_interpreter *interpreter; + const char *error; + struct sfnt_graphics_state state; + Lisp_Object regexp; + + /* If Vsfnt_uninstructable_family_regexp matches this font, then + return. */ + + regexp = Vsfnt_uninstructable_family_regexp; + + if (STRINGP (regexp) + && (fast_string_match_ignore_case (regexp, + desc->family) + >= 0)) + return; + + /* Load the cvt, fpgm and prep already read. */ + + cvt = info->cvt ; + fpgm = info->fpgm; + prep = info->prep; + + /* If both fpgm and prep are NULL, this font likely has no + instructions, so don't bother setting up the interpreter. */ + + if (!fpgm && !prep) + goto bail; + + /* If the interpreter does not use the operand stack at all, it is + useless. In addition, some broken fonts specify some unnecessary + instructions in prep and set head->max_stack_elements to 0. + + Don't create the interpreter in that case. */ + + if (!info->maxp->max_stack_elements) + goto bail; + + /* Now, create the interpreter using the limits in info->maxp and + info->head. CVT can be NULL. */ + + interpreter = sfnt_make_interpreter (info->maxp, cvt, info->head, + desc->tables->fvar, + info->font.pixel_size, + point_size); + + /* Bail if the interpreter couldn't be created. */ + if (!interpreter) + goto bail; + + if (fpgm) + { + /* Otherwise, evaluate the font and cvt programs. + + FIXME: make sure infinite loops inside these programs + cannot lock up Emacs. */ + + error = sfnt_interpret_font_program (interpreter, fpgm); + + if (error) + { + /* If an error occurs, log it to the *Messages* buffer. */ + message_with_string ("While interpreting font program: %s", + build_string (error), true); + goto bail1; + } + + /* Save the graphics state. */ + state = interpreter->state; + } + + if (prep) + { + /* This will overwrite state if the instruction control is set + appropriately. */ + error = sfnt_interpret_control_value_program (interpreter, prep, + &state); + + if (error) + { + /* If an error occurs, log it to the *Messages* buffer. */ + message_with_string ("While interpreting preprogram: %s", + build_string (error), true); + goto bail1; + } + } + + /* The interpreter has been properly set up. */ + info->fpgm = fpgm; + info->prep = prep; + info->cvt = cvt; + info->state = state; + info->interpreter = interpreter; + + return; + + bail1: + xfree (interpreter); + bail: + return; +} + +/* Free each of the tables opened by `sfnt_open_tables', and possibly + file descriptors as well. Then, free TABLES itself. */ + +static void +sfnt_close_tables (struct sfnt_font_tables *tables) +{ +#ifdef HAVE_MMAP + int rc; +#endif /* HAVE_MMAP */ + + xfree (tables->cmap); + xfree (tables->hhea); + xfree (tables->maxp); + xfree (tables->head); + xfree (tables->hmtx); +#ifdef HAVE_MMAP + if (tables->glyf_table_mapped) + { + rc = sfnt_unmap_glyf_table (tables->glyf); + + if (rc) + emacs_abort (); + } + else +#endif /* HAVE_MMAP */ + xfree (tables->glyf); + xfree (tables->loca_short); + xfree (tables->loca_long); + xfree (tables->prep); + xfree (tables->fpgm); + xfree (tables->cvt); + xfree (tables->fvar); + xfree (tables->avar); + xfree (tables->gvar); + xfree (tables->cvar); + xfree (tables->cmap_data); + + if (tables->uvs) + sfnt_free_uvs_context (tables->uvs); + +#ifdef HAVE_HARFBUZZ + /* Close the font file. */ + + if (tables->fd != -1) + { + emacs_close (tables->fd); + tables->fd = -1; + } + + /* Free its table directory. */ + xfree (tables->directory); + tables->directory = NULL; +#endif +} + +/* Open font tables associated with the specified font description + DESC. Return the font tables, or NULL upon failure. */ + +static struct sfnt_font_tables * +sfnt_open_tables (struct sfnt_font_desc *desc) +{ + struct sfnt_font_tables *tables; + struct sfnt_offset_subtable *subtable; + int fd, i; +#ifdef HAVE_MMAP + int rc; +#endif /* HAVE_MMAP */ + struct sfnt_cmap_encoding_subtable *subtables; + struct sfnt_cmap_encoding_subtable_data **data; + struct sfnt_cmap_format_14 *format14; + + tables = xzalloc (sizeof *tables); + + /* Open the font. */ + fd = emacs_open (desc->path, O_RDONLY, 0); + + if (fd == -1) + goto bail; + + /* Seek to the offset specified to the table directory. */ + + if (desc->offset + && lseek (fd, desc->offset, SEEK_SET) != desc->offset) + goto bail; + + /* Read the offset subtable. */ + subtable = sfnt_read_table_directory (fd); + + if (!subtable || (subtable == (struct sfnt_offset_subtable *) -1)) + goto bail1; + + /* Read required tables. This font backend is supposed to be used + mostly on devices with flash memory, so the order in which they + are read is insignificant. */ + + tables->cmap = sfnt_read_cmap_table (fd, subtable, &subtables, + &data); + if (!tables->cmap) + goto bail2; + + format14 = NULL; + tables->cmap_data + = sfntfont_select_cmap (tables->cmap, + subtables, data, + &tables->cmap_subtable, + &format14); + + if (format14) + { + /* Build a UVS context from this format 14 mapping table. A UVS + context contains each variation selector supported by the + font, and a list of ``non-default'' mappings between base + characters and variation glyph IDs. */ + + tables->uvs = sfnt_create_uvs_context (format14, fd); + xfree (format14); + } + + for (i = 0; i < tables->cmap->num_subtables; ++i) + { + if (data[i] != tables->cmap_data + /* format14 has already been freed. */ + && data[i] != (struct sfnt_cmap_encoding_subtable_data *) format14) + xfree (data[i]); + } + + xfree (subtables); + xfree (data); + + if (!tables->cmap_data) + goto bail3; + + /* Read the hhea, maxp, glyf, and head tables. */ + tables->hhea = sfnt_read_hhea_table (fd, subtable); + tables->maxp = sfnt_read_maxp_table (fd, subtable); + +#ifdef HAVE_MMAP + + /* First try to map the glyf table. If that fails, then read the + glyf table. */ + + tables->glyf = sfnt_map_glyf_table (fd, subtable); + + /* Next, if this fails, read the glyf table. */ + + if (!tables->glyf) +#endif /* HAVE_MMAP */ + tables->glyf = sfnt_read_glyf_table (fd, subtable); +#ifdef HAVE_MMAP + else + tables->glyf_table_mapped = true; +#endif /* HAVE_MMAP */ + + tables->head = sfnt_read_head_table (fd, subtable); + + /* If any of those tables couldn't be read, bail. */ + if (!tables->hhea || !tables->maxp || !tables->glyf + || !tables->head) + goto bail4; + + /* Now figure out which kind of loca table must be read based on + head->index_to_loc_format. */ + + if (tables->head->index_to_loc_format) + { + tables->loca_long + = sfnt_read_loca_table_long (fd, subtable); + + if (!tables->loca_long) + goto bail4; + } + else + { + tables->loca_short + = sfnt_read_loca_table_short (fd, subtable); + + if (!tables->loca_short) + goto bail4; + } + + /* Read the horizontal metrics table. */ + tables->hmtx = sfnt_read_hmtx_table (fd, subtable, + tables->hhea, + tables->maxp); + if (!tables->hmtx) + goto bail5; + + /* Read instruction related font tables. These might not be + present, which is OK, since instructing fonts is optional. */ + tables->prep = sfnt_read_prep_table (fd, subtable); + tables->fpgm = sfnt_read_fpgm_table (fd, subtable); + tables->cvt = sfnt_read_cvt_table (fd, subtable); + + /* Read distortion related tables. These might not be present. */ + tables->fvar = sfnt_read_fvar_table (fd, subtable); + tables->avar = sfnt_read_avar_table (fd, subtable); + tables->gvar = sfnt_read_gvar_table (fd, subtable); + + if (tables->cvt && tables->fvar) + tables->cvar = sfnt_read_cvar_table (fd, subtable, tables->fvar, + tables->cvt); + +#ifdef HAVE_HARFBUZZ + /* Now copy over the subtable if necessary, as it is needed to read + extra font tables required by HarfBuzz. */ + tables->directory = subtable; + tables->fd = fd; +#else /* !HAVE_HARFBUZZ */ + /* Otherwise, close the fd and free the table directory. */ + xfree (subtable); + emacs_close (fd); +#endif /* HAVE_HARFBUZZ */ + + return tables; + + bail5: + xfree (tables->loca_long); + xfree (tables->loca_short); + bail4: + xfree (tables->hhea); + xfree (tables->maxp); + +#ifdef HAVE_MMAP + if (tables->glyf_table_mapped) + { + rc = sfnt_unmap_glyf_table (tables->glyf); + + if (rc) + emacs_abort (); + } + else +#endif /* HAVE_MMAP */ + xfree (tables->glyf); + + xfree (tables->head); + + /* This comes under bail4 due to a peculiarity of how the four + tables above are validated. */ + xfree (tables->cmap_data); + bail3: + if (tables->uvs) + sfnt_free_uvs_context (tables->uvs); + + xfree (tables->cmap); + bail2: + xfree (subtable); + bail1: + emacs_close (fd); + bail: + xfree (tables); + return NULL; +} + +/* Open or reference font tables corresponding to the specified font + DESC. Return NULL upon failure. */ + +static struct sfnt_font_tables * +sfnt_reference_font_tables (struct sfnt_font_desc *desc) +{ + if (desc->refcount) + { + desc->refcount++; + return desc->tables; + } + + desc->tables = sfnt_open_tables (desc); + + if (!desc->tables) + return NULL; + + desc->refcount++; + return desc->tables; +} + +/* Dereference font tables corresponding to the specified font + DESC. */ + +static void +sfnt_dereference_font_tables (struct sfnt_font_desc *desc) +{ + if (!desc->refcount) + emacs_abort (); + + if (--desc->refcount) + return; + + sfnt_close_tables (desc->tables); + desc->tables = NULL; + return; +} + +/* Open the font corresponding to the font-entity FONT_ENTITY. Return + nil upon failure, else the opened font-object. */ + +Lisp_Object +sfntfont_open (struct frame *f, Lisp_Object font_entity, + int pixel_size) +{ + struct sfnt_font_info *font_info; + struct font *font; + struct sfnt_font_desc *desc; + Lisp_Object font_object; + struct charset *charset; + int point_size, instance, i; + Display_Info *dpyinfo; + struct sfnt_font_tables *tables; + Lisp_Object tem; + + if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0) + pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)); + else if (pixel_size == 0) + { + /* This bit was copied from xfont.c. The values might need + adjustment. */ + + if (FRAME_FONT (f)) + pixel_size = FRAME_FONT (f)->pixel_size; + else + pixel_size = 12; + } + + /* Now find the font description corresponding to FONT_ENTITY. */ + + tem = AREF (font_entity, FONT_EXTRA_INDEX); + if (NILP (tem)) + return Qnil; + + desc = xmint_pointer (XCDR (XCAR (tem))); + + /* Finally, see if a specific instance is associated with + FONT_ENTITY. */ + + instance = -1; + if (!NILP (XCDR (tem))) + instance = XFIXNUM (XCDR (XCAR (XCDR (tem)))); + + /* Build the font object. */ + font_object = font_make_object (VECSIZE (struct sfnt_font_info), + font_entity, pixel_size); + font_info = (struct sfnt_font_info *) XFONT_OBJECT (font_object); + + block_input (); + + /* Initialize all the font driver specific data. */ + + font_info->cmap = NULL; + font_info->hhea = NULL; + font_info->maxp = NULL; + font_info->head = NULL; + font_info->glyf = NULL; + font_info->hmtx = NULL; + font_info->loca_short = NULL; + font_info->loca_long = NULL; + font_info->cmap_data = NULL; + font_info->prep = NULL; + font_info->fpgm = NULL; + font_info->cvt = NULL; + font_info->uvs = NULL; + + font_info->outline_cache.next = &font_info->outline_cache; + font_info->outline_cache.last = &font_info->outline_cache; + font_info->outline_cache_size = 0; + font_info->raster_cache.next = &font_info->raster_cache; + font_info->raster_cache.last = &font_info->raster_cache; + font_info->raster_cache_size = 0; + font_info->interpreter = NULL; + font_info->scale = 0; + font_info->instance = -1; + font_info->blend.coords = NULL; +#ifdef HAVE_MMAP + font_info->glyf_table_mapped = false; +#endif /* HAVE_MMAP */ +#ifdef HAVE_HARFBUZZ + font_info->hb_font = NULL; + font_info->fd = -1; + font_info->directory = NULL; +#endif /* HAVE_HARFBUZZ */ + + /* Read required tables. This font backend is supposed to be used + mostly on devices with flash memory, so the order in which they + are read is insignificant. */ + + tables = sfnt_reference_font_tables (desc); + + if (!tables) + goto bail; + + /* Copy fields from the table structure to the font for fast + access. */ + font_info->cmap = tables->cmap; + font_info->hhea = tables->hhea; + font_info->maxp = tables->maxp; + font_info->head = tables->head; + font_info->hmtx = tables->hmtx; + font_info->glyf = tables->glyf; + font_info->loca_short = tables->loca_short; + font_info->loca_long = tables->loca_long; + font_info->prep = tables->prep; + font_info->fpgm = tables->fpgm; + font_info->cvt = tables->cvt ; + font_info->cmap_data = tables->cmap_data; + font_info->cmap_subtable = tables->cmap_subtable; + font_info->uvs = tables->uvs; + + /* Calculate the font's scaling factor. */ + font_info->scale = sfnt_get_scale (font_info->head, pixel_size); + + /* Fill in font data. */ + font = &font_info->font; + font->pixel_size = pixel_size; + font->driver = sfnt_font_driver; + font->encoding_charset = font->repertory_charset = -1; + + /* Figure out which character set to use. */ + charset = sfntfont_charset_for_cmap (font_info->cmap_subtable); + + if (!charset) + goto bail6; + + /* Set the character set IDs. */ + font->encoding_charset = charset->id; + font->repertory_charset = charset->id; + + /* Figure out the font ascent and descent. */ + font->ascent + = ceil (font_info->hhea->ascent + * pixel_size + * (1.0 / font_info->head->units_per_em)); + font->descent + = ceil ((-font_info->hhea->descent) + * pixel_size + * (1.0 / font_info->head->units_per_em)); + font->height = font->ascent + font->descent; + + /* Set font->max_width to the maximum advance width. */ + font->max_width = (font_info->hhea->advance_width_max + * pixel_size * 1.0 / font_info->head->units_per_em); + + /* Set generic attributes such as type and style. */ + ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name); + ASET (font_object, FONT_FOUNDRY_INDEX, desc->designer); + ASET (font_object, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil)); + ASET (font_object, FONT_ADSTYLE_INDEX, desc->adstyle); + ASET (font_object, FONT_REGISTRY_INDEX, + sfntfont_registry_for_desc (desc)); + + /* Size of 0 means the font is scalable. */ + ASET (font_object, FONT_SIZE_INDEX, make_fixnum (0)); + ASET (font_object, FONT_AVGWIDTH_INDEX, make_fixnum (0)); + ASET (font_object, FONT_SPACING_INDEX, make_fixnum (desc->spacing)); + + /* Set the font style. */ + + FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX, + make_fixnum (desc->width)); + FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX, + make_fixnum (desc->weight)); + FONT_SET_STYLE (font_object, FONT_SLANT_INDEX, + make_fixnum (desc->slant)); + + /* Clear various offsets. */ + font_info->font.baseline_offset = 0; + font_info->font.relative_compose = 0; + font_info->font.default_ascent = 0; + font_info->font.vertical_centering = 0; + + if (!desc->underline_position_set) + { + font_info->font.underline_position = -1; + font_info->font.underline_thickness = 0; + } + else + { + font_info->font.underline_position + = sfnt_coerce_fixed (-desc->underline_position + * font_info->scale) + 0.5; + font_info->font.underline_thickness + = sfnt_coerce_fixed (desc->underline_thickness + * font_info->scale) + 0.5; + } + + /* Now try to set up grid fitting for this font. */ + dpyinfo = FRAME_DISPLAY_INFO (f); + point_size = PIXEL_TO_POINT (pixel_size, (dpyinfo->resx + * dpyinfo->resy + / 2)); + sfntfont_setup_interpreter (font_info, desc, point_size); + + /* If an instance was specified and the font is distortable, set up + the blend. */ + + if (instance != -1 + && desc->tables->fvar && desc->tables->gvar + /* Make sure the instance is within range. */ + && instance < desc->tables->fvar->instance_count) + { + tem = AREF (desc->instances, instance); + + if (!NILP (tem)) + { + sfnt_init_blend (&font_info->blend, desc->tables->fvar, + desc->tables->gvar, desc->tables->avar, + desc->tables->cvar); + + /* Copy over the coordinates. */ + for (i = 0; i < desc->tables->fvar->axis_count; ++i) + font_info->blend.coords[i] + = desc->tables->fvar->instance[instance].coords[i]; + + sfnt_normalize_blend (&font_info->blend); + + /* Test whether or not the instance is actually redundant, + as all of its axis are at their default values. If so, + free the instance. */ + + for (i = 0; i < desc->tables->fvar->axis_count; ++i) + { + if (font_info->blend.norm_coords[i]) + break; + } + + if (i == desc->tables->fvar->axis_count) + { + sfnt_free_blend (&font_info->blend); + goto cancel_blend; + } + + /* If an interpreter was specified, distort it now. */ + + if (font_info->interpreter) + sfnt_vary_interpreter (font_info->interpreter, + &font_info->blend); + + font_info->instance = instance; + + /* Replace the style information with that of the + instance. */ + + FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX, + AREF (tem, 2)); + FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX, + AREF (tem, 3)); + FONT_SET_STYLE (font_object, FONT_SLANT_INDEX, + AREF (tem, 4)); + ASET (font_object, FONT_ADSTYLE_INDEX, AREF (tem, 1)); + } + } + + cancel_blend: + + /* Find out the minimum, maximum and average widths. */ + sfntfont_probe_widths (font_info); + + /* Calculate the xfld name. */ + font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil, Qt); + +#ifdef HAVE_HARFBUZZ + /* HarfBuzz will potentially read font tables after the font has + been opened by Emacs. Keep the font open, and record its offset + subtable. */ + font_info->fd = tables->fd; + font_info->directory = tables->directory; +#endif /* HAVE_HARFBUZZ */ + + /* Set font->desc so that font tables can be dereferenced if + anything goes wrong. */ + font_info->desc = desc; + +#ifdef HAVE_MMAP + /* Link the font onto the font table. */ + font_info->next = open_fonts; + open_fonts = font_info; +#endif /* HAVE_MMAP */ + + /* Now ascertain if vertical centering is desired by matching the + font XLFD against vertical-centering-font-regexp. */ + + if (!NILP (font->props[FONT_NAME_INDEX])) + font->vertical_centering + = (STRINGP (Vvertical_centering_font_regexp) + && (fast_string_match_ignore_case + (Vvertical_centering_font_regexp, + font->props[FONT_NAME_INDEX]) >= 0)); + + /* Set the name of the font file. */ + font->props[FONT_FILE_INDEX] + = DECODE_FILE (build_unibyte_string (desc->path)); + + /* Encapsulate some information on the font useful while debugging + (along with being informative in general) in the font name. */ + + AUTO_STRING (format, "%s %s interpreted: %s upem: %s charset: %s" + " instance: %s"); + font->props[FONT_FULLNAME_INDEX] + = CALLN (Fformat, format, desc->family, desc->style, + font_info->interpreter ? Qt : Qnil, + make_fixnum (font_info->head->units_per_em), + CHARSET_NAME (charset), + make_fixnum (instance)); + + /* All done. */ + unblock_input (); + return font_object; + + bail6: + sfnt_dereference_font_tables (desc); + font_info->desc = NULL; + bail: + unblock_input (); + return Qnil; +} + + + +/* Metrics computation and other similar font backend functions. */ + +/* Return the glyph code corresponding to C inside the font-object + FONT. Value is the glyph code upon success, else + FONT_INVALID_CODE. */ + +unsigned int +sfntfont_encode_char (struct font *font, int c) +{ + sfnt_glyph glyph; + + /* Now look up the glyph. */ + glyph = sfntfont_lookup_glyph ((struct sfnt_font_info *) font, c); + + if (!glyph) + return FONT_INVALID_CODE; + + return glyph; +} + +/* Measure the single glyph GLYPH in the font FONT and return its + metrics in *PCM. + + Instruct the glyph if possible. + + Value is 0 upon success, 1 otherwise. */ + +static int +sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, + struct font_metrics *pcm) +{ + struct sfnt_glyph_metrics metrics; + struct sfnt_glyph_outline *outline; + + /* Now get the glyph outline, which is required to obtain the rsb, + ascent and descent. */ + outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache, + font->scale, + &font->outline_cache_size, + &font->blend, + font->instance, + font->glyf, font->head, + font->hmtx, font->hhea, + font->maxp, + font->loca_short, + font->loca_long, + font->interpreter, &metrics, + &font->state); + + if (!outline) + return 1; + + /* The left side bearing has already been floored. */ + pcm->lbearing = metrics.lbearing / 65536; + pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) / 65536; + + /* The advance is already rounded; ceil the ascent and descent. */ + pcm->width = metrics.advance / 65536; + pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) / 65536; + pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) / 65536; + + sfntfont_dereference_outline (outline); + return 0; +} + +/* Return the total text extents of NGLYPHS glyphs given as CODE in + the single font metrics array METRICS. */ + +void +sfntfont_text_extents (struct font *font, const unsigned int *code, + int nglyphs, struct font_metrics *metrics) +{ + int i, total_width; + struct font_metrics pcm; + + total_width = 0; + + /* First clear the metrics array. */ + memset (metrics, 0, sizeof *metrics); + + /* Get the metrcs one by one, then sum them up. */ + for (i = 0; i < nglyphs; ++i) + { + if (!sfntfont_measure_pcm ((struct sfnt_font_info *) font, + code[i], &pcm)) + { + /* Add the per-char metric (PCM) to the metrics in + METRICS. */ + + if (total_width + pcm.lbearing < metrics->lbearing) + metrics->lbearing = total_width + pcm.lbearing; + + if (total_width + pcm.rbearing > metrics->rbearing) + metrics->rbearing = total_width + pcm.rbearing; + + if (pcm.ascent > metrics->ascent) + metrics->ascent = pcm.ascent; + + if (pcm.descent > metrics->descent) + metrics->descent = pcm.descent; + + total_width += pcm.width; + } + } + + metrics->width = total_width; +} + +/* Close the font FONT, discarding all tables inside it and + dereferencing all cached outlines and rasters. */ + +void +sfntfont_close (struct font *font) +{ + struct sfnt_font_info *info; +#ifdef HAVE_MMAP + struct sfnt_font_info **next; +#endif /* HAVE_MMAP */ + + info = (struct sfnt_font_info *) font; + + /* If info->desc is still set, dereference the font tables. */ + if (info->desc) + sfnt_dereference_font_tables (info->desc); + info->desc = NULL; + + /* Free the interpreter, which is created on a per font basis. */ + xfree (info->interpreter); + + /* Clear these fields. It seems that close can be called twice, + once during font driver destruction, and once during GC. */ + + info->cmap = NULL; + info->hhea = NULL; + info->maxp = NULL; + info->head = NULL; + info->hhea = NULL; + info->glyf = NULL; + info->loca_short = NULL; + info->loca_long = NULL; + info->cmap_data = NULL; + info->prep = NULL; + info->fpgm = NULL; + info->cvt = NULL; + info->interpreter = NULL; + info->uvs = NULL; + + /* Deinitialize the blend. */ + if (info->instance != -1 && info->blend.coords) + sfnt_free_blend (&info->blend); + info->instance = -1; + +#ifdef HAVE_MMAP + + /* Unlink INFO. */ + + next = &open_fonts; + while (*next && (*next) != info) + next = &(*next)->next; + + if (*next) + *next = info->next; + info->next = NULL; + +#endif /* HAVE_MMAP */ + +#ifdef HAVE_HARFBUZZ + /* These fields will be freed or closed by + sfnt_dereference_font_tables, but clear them here for good + measure. */ + info->directory = NULL; + info->fd = -1; + + /* Free any hb_font created. */ + + if (info->hb_font) + { + hb_font_destroy (info->hb_font); + info->hb_font = NULL; + } +#endif + + sfntfont_free_outline_cache (&info->outline_cache); + sfntfont_free_raster_cache (&info->raster_cache); +} + + + +/* Glyph display. */ + +/* Function called to actually draw rasters to the glass. */ +static sfntfont_put_glyph_proc sfnt_put_glyphs; + +/* Draw glyphs in S->char2b starting from FROM to TO, with the origin + at X and baseline at Y. Fill the background from X, Y + + FONT_DESCENT to X + S->background_width, Y - FONT_ASCENT with the + background color if necessary. Use the foreground and background + colors in S->gc. */ + +int +sfntfont_draw (struct glyph_string *s, int from, int to, + int x, int y, bool with_background) +{ + int length; + struct sfnt_raster **rasters; + int *x_coords, current_x, i; + struct sfnt_glyph_outline *outline; + struct font *font; + struct sfnt_font_info *info; + struct sfnt_glyph_metrics metrics; + + length = to - from; + font = s->font; + info = (struct sfnt_font_info *) font; + + rasters = alloca (length * sizeof *rasters); + x_coords = alloca (length * sizeof *x_coords); + current_x = x; + + /* Get rasters and outlines for them. */ + for (i = from; i < to; ++i) + { + /* Look up the outline. */ + outline = sfntfont_get_glyph_outline (s->char2b[i], + &info->outline_cache, + info->scale, + &info->outline_cache_size, + &info->blend, + info->instance, + info->glyf, info->head, + info->hmtx, info->hhea, + info->maxp, + info->loca_short, + info->loca_long, + info->interpreter, + &metrics, + &info->state); + x_coords[i - from] = 0; + + if (!outline) + { + rasters[i - from] = NULL; + continue; + } + + /* Rasterize the outline. */ + rasters[i - from] = sfntfont_get_glyph_raster (s->char2b[i], + &info->raster_cache, + outline, + &info->raster_cache_size); + sfntfont_dereference_outline (outline); + + if (!rasters[i - from]) + continue; + + /* Now work out where to put the outline. */ + x_coords[i - from] = current_x; + + if (s->padding_p) + current_x += 1; + else + current_x += metrics.advance / 65536; + } + + /* Call the window system function to put the glyphs to the + frame. */ + sfnt_put_glyphs (s, from, to, x, y, with_background, + rasters, x_coords); + + /* Dereference all the rasters. */ + for (i = 0; i < from - to; ++i) + { + if (rasters[i]) + sfntfont_dereference_raster (rasters[i]); + } + + return 1; +} + + + +/* Other callbacks. */ + +/* Return a list of each font family known to Emacs. F is supposed to + be a frame but is ignored. */ + +Lisp_Object +sfntfont_list_family (struct frame *f) +{ + Lisp_Object families, tem, next; + struct sfnt_font_desc *desc; + + families = Qnil; + + for (desc = system_fonts; desc; desc = desc->next) + /* Add desc->family to the list. */ + families = Fcons (desc->family, families); + + /* Sort families in preparation for removing duplicates. */ + families = CALLN (Fsort, families, Qstring_lessp); + + /* Remove each duplicate within families. */ + + tem = families; + while (!NILP (tem) && !NILP ((next = XCDR (tem)))) + { + /* If the two strings are equal. */ + if (!NILP (Fstring_equal (XCAR (tem), XCAR (next)))) + /* Set tem's cdr to the cons after the next item. */ + XSETCDR (tem, XCDR (next)); + else + /* Otherwise, start considering the next item. */ + tem = next; + } + + /* Intern each font family. */ + + tem = families; + + FOR_EACH_TAIL (tem) + XSETCAR (tem, Fintern (XCAR (tem), Qnil)); + + return families; +} + + + +/* Unicode Variation Selector (UVS) support. This is typically + required for Harfbuzz. */ + +/* Given a FONT object, a character C, and VARIATIONS, return the + number of non-default variation glyphs, and their glyph ids in + VARIATIONS. + + For each variation selector character K with a non-default glyph in + the variation selector range 0xFE00 to 0xFE0F, set variations[K - + 0xFE0] to its ID. + + For each variation selector character K with a non-default glyph in + the variation selector range 0xE0100 to 0xE01EF, set variations[K - + 0xE0100 + 16] to its ID. + + If value is more than 0, set all other members of VARIATIONS to 0. + Else, the contents of VARIATIONS are undefined. */ + +int +sfntfont_get_variation_glyphs (struct font *font, int c, + unsigned variations[256]) +{ + struct sfnt_font_info *info; + size_t i, index; + int n; + struct sfnt_mapped_variation_selector_record *record; + sfnt_glyph default_glyph; + + info = (struct sfnt_font_info *) font; + n = 0; + + /* Return 0 if there is no UVS mapping table. */ + + if (!info->uvs) + return 0; + + /* Clear the variations array. */ + + memset (variations, 0, sizeof *variations * 256); + + /* Find the first 0xFExx selector. */ + + i = 0; + while (i < info->uvs->num_records + && info->uvs->records[i].selector < 0xfe00) + ++i; + + /* Get the glyph represented by C, used when C is present within a + default value table. */ + + default_glyph = sfntfont_lookup_glyph (info, c); + + /* Fill in selectors 0 to 15. */ + + while (i < info->uvs->num_records + && info->uvs->records[i].selector <= 0xfe0f) + { + record = &info->uvs->records[i]; + index = info->uvs->records[i].selector - 0xfe00 + 16; + + /* Handle invalid unsorted tables. */ + + if (record->selector < 0xfe00) + return 0; + + /* If there are default mappings in this record, ascertain if + this glyph matches one of them. */ + + if (record->default_uvs + && sfnt_is_character_default (record->default_uvs, c)) + { + variations[index] = default_glyph; + + if (default_glyph) + ++n; + + goto next_selector; + } + + /* If record has no non-default mappings, continue on to the + next selector. */ + + if (!record->nondefault_uvs) + goto next_selector; + + /* Find the glyph ID associated with C and put it in + VARIATIONS. */ + + variations[index] + = sfnt_variation_glyph_for_char (record->nondefault_uvs, c); + + if (variations[index]) + ++n; + + next_selector: + ++i; + } + + /* Find the first 0xE0100 selector. */ + + i = 0; + while (i < info->uvs->num_records + && info->uvs->records[i].selector < 0xe0100) + ++i; + + /* Fill in selectors 16 to 255. */ + + while (i < info->uvs->num_records + && info->uvs->records[i].selector <= 0xe01ef) + { + record = &info->uvs->records[i]; + index = info->uvs->records[i].selector - 0xe0100 + 16; + + /* Handle invalid unsorted tables. */ + + if (record->selector < 0xe0100) + return 0; + + /* If there are default mappings in this record, ascertain if + this glyph matches one of them. */ + + if (record->default_uvs + && sfnt_is_character_default (record->default_uvs, c)) + { + variations[index] = default_glyph; + + if (default_glyph) + ++n; + + goto next_selector_1; + } + + /* If record has no non-default mappings, continue on to the + next selector. */ + + if (!record->nondefault_uvs) + goto next_selector_1; + + /* Find the glyph ID associated with C and put it in + VARIATIONS. */ + + variations[index] + = sfnt_variation_glyph_for_char (record->nondefault_uvs, c); + + if (variations[index]) + ++n; + + next_selector_1: + ++i; + } + + return n; +} + + + +/* mmap specific stuff. */ + +#ifdef HAVE_MMAP + +/* Return whether or not ADDR lies in a mapped glyph, and bus faults + should be ignored. */ + +bool +sfntfont_detect_sigbus (void *addr) +{ + struct sfnt_font_info *info; + + for (info = open_fonts; info; info = info->next) + { + if (info->glyf_table_mapped + && (unsigned char *) addr >= info->glyf->glyphs + && (unsigned char *) addr < (info->glyf->glyphs + + info->glyf->size)) + return true; + } + + return false; +} + +#endif /* HAVE_MMAP */ + + + +/* Harfbuzz font support. */ + +#ifdef HAVE_HARFBUZZ + +#ifdef HAVE_MMAP + +/* Unmap the specified table. */ + +static void +sfntfont_unmap_blob (void *ptr) +{ + if (sfnt_unmap_table (ptr)) + emacs_abort (); + + xfree (ptr); +} + +#endif /* HAVE_MMAP */ + +/* Given a font DATA and a tag TAG, return the data of the + corresponding font table as a HarfBuzz blob. */ + +static hb_blob_t * +sfntfont_get_font_table (hb_face_t *face, hb_tag_t tag, void *data) +{ + size_t size; + struct sfnt_font_info *info; +#ifdef HAVE_MMAP + struct sfnt_mapped_table *table; + hb_blob_t *blob; + + info = data; + table = xmalloc (sizeof *table); + + if (!sfnt_map_table (info->fd, info->directory, tag, + table)) + { + /* Create an hb_blob_t and return it. + TODO: record this mapping properly so that SIGBUS can + be handled. */ + + blob = hb_blob_create (table->data, table->length, + HB_MEMORY_MODE_READONLY, + table, sfntfont_unmap_blob); + + /* Note that sfntfont_unmap_blob will be called if the empty + blob is returned. */ + return blob; + } + + xfree (table); +#else /* !HAVE_MMAP */ + + /* Try to read the table conventionally. */ + info = data; +#endif /* HAVE_MMAP */ + + data = sfnt_read_table (info->fd, info->directory, tag, + &size); + + if (!data) + return NULL; + + return hb_blob_create (data, size, HB_MEMORY_MODE_WRITABLE, + data, xfree); +} + +/* Create or return a HarfBuzz font object corresponding to the + specified FONT. Return the scale to convert between fwords and + pixels in POSITION_UNIT. */ + +hb_font_t * +sfntfont_begin_hb_font (struct font *font, double *position_unit) +{ + struct sfnt_font_info *info; + hb_face_t *face; + int factor; + + info = (struct sfnt_font_info *) font; + + if (info->hb_font) + { + /* Calculate the scale factor. */ + *position_unit = 1.0 / 64.0; + return info->hb_font; + } + + /* Create a face and then a font. */ + face = hb_face_create_for_tables (sfntfont_get_font_table, font, + NULL); + + if (hb_face_get_glyph_count (face) > 0) + { + info->hb_font = hb_font_create (face); + if (!info->hb_font) + goto bail; + + factor = font->pixel_size; + + /* Set the scale and PPEM values. */ + hb_font_set_scale (info->hb_font, factor * 64, factor * 64); + hb_font_set_ppem (info->hb_font, factor, factor); + +#ifdef HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE + /* Set the instance if this is a distortable font. */ + if (info->instance != -1) + hb_font_set_var_named_instance (info->hb_font, + info->instance); +#endif /* HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE */ + + /* This is needed for HarfBuzz before 2.0.0; it is the default + in later versions. */ + hb_ot_font_set_funcs (info->hb_font); + } + + bail: + hb_face_destroy (face); + + /* Calculate the scale factor. */ + *position_unit = 1.0 / 64.0; + return info->hb_font; +} + +#endif /* HAVE_HARFBUZZ */ + + + +void +syms_of_sfntfont (void) +{ + DEFSYM (Qutf_16be, "utf-16be"); + DEFSYM (Qmac_roman, "mac-roman"); + DEFSYM (Qchinese_big5, "chinese-big5"); + DEFSYM (Qunicode_bmp, "unicode-bmp"); + DEFSYM (Qucs, "ucs"); + DEFSYM (Qjapanese_jisx0208, "japanese-jisx0208"); + DEFSYM (Qgbk, "gbk"); + DEFSYM (Qkorean_ksc5601, "korean-ksc5601"); + DEFSYM (Qapple_roman, "apple-roman"); + DEFSYM (Qjisx0208_1983_0, "jisx0208.1983-0"); + DEFSYM (Qksc5601_1987_0, "ksc5601.1987-0"); + DEFSYM (Qzh, "zh"); + DEFSYM (Qja, "ja"); + DEFSYM (Qko, "ko"); + DEFSYM (Qfont_instance, "font-instance"); + + /* Char-table purpose. */ + DEFSYM (Qfont_lookup_cache, "font-lookup-cache"); + + /* Default foundry name. */ + DEFSYM (Qmisc, "misc"); + + /* Predicated employed for sorting font family lists. */ + DEFSYM (Qstring_lessp, "string-lessp"); + + /* Set up staticpros. */ + sfnt_vendor_name = Qnil; + staticpro (&sfnt_vendor_name); + + /* This variable is supposed to be set by the platform specific part + of the font backend. */ + DEFVAR_LISP ("sfnt-default-family-alist", Vsfnt_default_family_alist, + doc: /* Alist between "emulated" and actual font family names. +Much Emacs code assumes that font families named "Monospace" and "Sans +Serif" exist, and map to the default monospace and Sans Serif fonts on +a system. When the `sfnt' font driver is asked to look for a font +with one of the families in this alist, it uses its value instead. */); + Vsfnt_default_family_alist = Qnil; + + DEFVAR_LISP ("sfnt-uninstructable-family-regexp", + Vsfnt_uninstructable_family_regexp, + doc: /* Regexp matching font families whose glyphs must not be instructed. +If nil, instruction code supplied by all fonts will be executed. This +variable takes effect when a font entity is opened, not after, and +therefore won't affect the scaling of realized faces until their +frames' font caches are cleared (see `clear-font-cache'). + +TrueType fonts incorporate instruction code executed to fit each glyph +to a pixel grid, so as to improve the visual fidelity of each glyph by +eliminating artifacts and chance effects consequent upon the direct +upscaling of glyph outline data. Instruction code is occasionally +incompatible with Emacs and must be disregarded. */); + Vsfnt_uninstructable_family_regexp = Qnil; + + DEFVAR_BOOL ("sfnt-raster-glyphs-exactly", sfnt_raster_glyphs_exactly, + doc: /* How font glyph outlines should be converted to graphics. +If non-nil, glyphs will be displayed in a more precise manner, at the +cost of performance on devices where floating-point math operations +are slow. */); + sfnt_raster_glyphs_exactly = true; +} + +void +mark_sfntfont (void) +{ + struct sfnt_font_desc *desc; + + /* Mark each font desc. */ + for (desc = system_fonts; desc; desc = desc->next) + { + mark_object (desc->family); + mark_object (desc->style); + mark_object (desc->adstyle); + mark_object (desc->instances); + mark_object (desc->languages); + mark_object (desc->registry); + mark_object (desc->char_cache); + mark_object (desc->designer); + } +} + +void +init_sfntfont (void) +{ + +} + + + +/* Initialize the sfntfont font driver. VENDOR_TYPE is the type of + all font entities created. DRIVER is the font driver that is saved + in font objects. PUT_GLYPHS is a function that is called with 8 + arguments, S, FROM, TO, X, Y, WITH_BACKGROUND, RASTERS, and + X_COORDS, and should draw all the rasters in RASTERS to S->f, + originating at X_COORDS[i], Y, along with filling the background if + WITH_BACKGROUND is specified. */ + +void +init_sfntfont_vendor (Lisp_Object vendor_name, + const struct font_driver *driver, + sfntfont_put_glyph_proc put_glyphs) +{ + sfnt_vendor_name = vendor_name; + sfnt_font_driver = driver; + sfnt_put_glyphs = put_glyphs; +} |