summaryrefslogtreecommitdiff
path: root/src/sfntfont.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sfntfont.c')
-rw-r--r--src/sfntfont.c4229
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;
+}