From c2f5bfd68d0d6bf0e9a176a3a6b110d4a4c048b6 Mon Sep 17 00:00:00 2001 From: Kenichi Handa Date: Tue, 6 Jun 2006 03:47:13 +0000 Subject: [PATCH] New file. --- src/font.c | 2571 +++++++++++++++++++++++++++++++++++++++++++++++++ src/font.h | 479 +++++++++ src/ftfont.c | 731 ++++++++++++++ src/ftxfont.c | 346 +++++++ src/xfont.c | 868 +++++++++++++++++ src/xftfont.c | 552 +++++++++++ 6 files changed, 5547 insertions(+) create mode 100644 src/font.c create mode 100644 src/font.h create mode 100644 src/ftfont.c create mode 100644 src/ftxfont.c create mode 100644 src/xfont.c create mode 100644 src/xftfont.c diff --git a/src/font.c b/src/font.c new file mode 100644 index 0000000000..eaf2ac0a84 --- /dev/null +++ b/src/font.c @@ -0,0 +1,2571 @@ +/* font.c -- "Font" primitives. + Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H13PRO009 + +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 2, 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; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include +#include +#include +#include + +#include "lisp.h" +#include "buffer.h" +#include "frame.h" +#include "dispextern.h" +#include "charset.h" +#include "character.h" +#include "composite.h" +#include "fontset.h" +#include "font.h" + +#define FONT_DEBUG + +#ifdef FONT_DEBUG +#undef xassert +#define xassert(X) do {if (!(X)) abort ();} while (0) +#else +#define xassert(X) (void) 0 +#endif + +int enable_font_backend; + +Lisp_Object Qfontp; + +/* Like CHECK_FONT_SPEC but also validate properties of the font-spec, + and set X to the validated result. */ + +#define CHECK_VALIDATE_FONT_SPEC(x) \ + do { \ + if (! FONT_SPEC_P (x)) x = wrong_type_argument (Qfont, x); \ + x = font_prop_validate (x); \ + } while (0) + +/* Number of pt per inch (from the TeXbook). */ +#define PT_PER_INCH 72.27 + +/* Return a pixel size corresponding to POINT size (1/10 pt unit) on + resolution RESY. */ +#define POINT_TO_PIXEL(POINT, RESY) ((POINT) * (RESY) / PT_PER_INCH / 10 + 0.5) + +#define PIXEL_TO_POINT(PIXEL, RESY) ((PIXEL) * PT_PER_INCH * 10 / (RESY) + 0.5) + +/* Special string of zero length. It is used to specify a NULL name + in a font properties (e.g. adstyle). We don't use the symbol of + NULL name because it's confusing (Lisp printer prints nothing for + it). */ +Lisp_Object null_string; + +/* Special vector of zero length. This is repeatedly used by (struct + font_driver *)->list when a specified font is not found. */ +Lisp_Object null_vector; + +/* Vector of 3 elements. Each element is an alist for one of font + style properties (weight, slant, width). The alist contains a + mapping between symbolic property values (e.g. `medium' for weight) + and numeric property values (e.g. 100). So, it looks like this: + [((thin . 0) ... (heavy . 210)) + ((ro . 0) ... (ot . 210)) + ((ultracondensed . 50) ... (wide . 200))] */ +static Lisp_Object font_style_table; + +/* Alist of font family vs the corresponding aliases. + Each element has this form: + (FAMILY ALIAS1 ALIAS2 ...) */ + +static Lisp_Object font_family_alist; + +/* Symbols representing keys of normal font properties. */ +extern Lisp_Object QCtype, QCfamily, QCweight, QCslant, QCwidth, QCsize, QCname; +Lisp_Object QCfoundry, QCadstyle, QCregistry, QCextra; +/* Symbols representing keys of font extra info. */ +Lisp_Object QCotf, QClanguage, QCscript; + +/* List of all font drivers. All font-backends (XXXfont.c) call + add_font_driver in syms_of_XXXfont to register the font-driver + here. */ +static struct font_driver_list *font_driver_list; + +static Lisp_Object prop_name_to_numeric P_ ((enum font_property_index, + Lisp_Object)); +static Lisp_Object prop_numeric_to_name P_ ((enum font_property_index, int)); +static Lisp_Object font_open_entity P_ ((FRAME_PTR, Lisp_Object, int)); + +/* Number of registered font drivers. */ +static int num_font_drivers; + +/* Return a numeric value corresponding to PROP's NAME (symbol). If + NAME is not registered in font_style_table, return Qnil. PROP must + be one of FONT_{WEIGHT|SLANT|SWIDTH}_INDEX. */ + +static Lisp_Object +prop_name_to_numeric (prop, name) + enum font_property_index prop; + Lisp_Object name; +{ + int table_index = prop - FONT_WEIGHT_INDEX; + Lisp_Object val; + + val = assq_no_quit (name, AREF (font_style_table, table_index)); + return (NILP (val) ? Qnil : XCDR (val)); +} + + +/* Return a name (symbol) corresponding to PROP's NUMERIC value. If + no name is registered for NUMERIC in font_style_table, return a + symbol of integer name (e.g. `123'). PROP must be one of + FONT_{WEIGHT|SLANT|SWIDTH}_INDEX. */ + +static Lisp_Object +prop_numeric_to_name (prop, numeric) + enum font_property_index prop; + int numeric; +{ + int table_index = prop - FONT_WEIGHT_INDEX; + Lisp_Object table = AREF (font_style_table, table_index); + char buf[10]; + + while (! NILP (table)) + { + if (XINT (XCDR (XCAR (table))) >= numeric) + { + if (XINT (XCDR (XCAR (table))) == numeric) + return XCAR (XCAR (table)); + else + break; + } + table = XCDR (table); + } + sprintf (buf, "%d", numeric); + return intern (buf); +} + + +/* Return a symbol whose name is STR (length LEN). If STR contains + uppercase letters, downcase them in advance. */ + +Lisp_Object +intern_downcase (str, len) + char *str; + int len; +{ + char *buf; + int i; + + for (i = 0; i < len; i++) + if (isupper (str[i])) + break; + if (i == len) + return Fintern (make_unibyte_string (str, len), Qnil); + buf = alloca (len); + if (! buf) + return Fintern (null_string, Qnil); + bcopy (str, buf, len); + for (; i < len; i++) + if (isascii (buf[i])) + buf[i] = tolower (buf[i]); + return Fintern (make_unibyte_string (buf, len), Qnil); +} + +extern Lisp_Object Vface_alternative_font_family_alist; + +static void +build_font_family_alist () +{ + Lisp_Object alist = Vface_alternative_font_family_alist; + + for (; CONSP (alist); alist = XCDR (alist)) + { + Lisp_Object tail, elt; + + for (tail = XCAR (alist), elt = Qnil ; CONSP (tail); tail = XCDR (tail)) + elt = nconc2 (elt, Fcons (Fintern (XCAR (tail), Qnil), Qnil)); + font_family_alist = Fcons (elt, font_family_alist); + } +} + + +/* Font property validater. */ + +static Lisp_Object +font_prop_validate_type (prop, val) + enum font_property_index prop; + Lisp_Object val; +{ + return (SYMBOLP (val) ? val : Qerror); +} + +static Lisp_Object +font_prop_validate_symbol (prop, val) + enum font_property_index prop; + Lisp_Object val; +{ + if (STRINGP (val)) + val = (SCHARS (val) == 0 ? null_string + : intern_downcase ((char *) SDATA (val), SBYTES (val))); + else if (SYMBOLP (val)) + { + if (SCHARS (SYMBOL_NAME (val)) == 0) + val = null_string; + } + else + val = Qerror; + return val; +} + +static Lisp_Object +font_prop_validate_style (prop, val) + enum font_property_index prop; + Lisp_Object val; +{ + if (! INTEGERP (val)) + { + if (STRINGP (val)) + val = intern_downcase ((char *) SDATA (val), SBYTES (val)); + if (! SYMBOLP (val)) + val = Qerror; + else + { + val = prop_name_to_numeric (prop, val); + if (NILP (val)) + val = Qerror; + } + } + return val; +} + +static Lisp_Object +font_prop_validate_size (prop, val) + enum font_property_index prop; + Lisp_Object val; +{ + return (NATNUMP (val) || (FLOATP (val) && XFLOAT_DATA (val) >= 0) + ? val : Qerror); +} + +static Lisp_Object +font_prop_validate_extra (prop, val) + enum font_property_index prop; + Lisp_Object val; +{ + Lisp_Object tail; + + for (tail = val; CONSP (tail); tail = XCDR (tail)) + { + Lisp_Object key = Fcar (XCAR (tail)), this_val = Fcdr (XCAR (tail)); + + if (NILP (this_val)) + return Qnil; + if (EQ (key, QClanguage)) + if (! SYMBOLP (this_val)) + { + for (; CONSP (this_val); this_val = XCDR (this_val)) + if (! SYMBOLP (XCAR (this_val))) + return Qerror; + if (! NILP (this_val)) + return Qerror; + } + if (EQ (key, QCotf)) + if (! STRINGP (this_val)) + return Qerror; + } + return (NILP (tail) ? val : Qerror); +} + + +struct +{ + Lisp_Object *key; + Lisp_Object (*validater) P_ ((enum font_property_index prop, + Lisp_Object val)); +} font_property_table[FONT_SPEC_MAX] = + { { &QCtype, font_prop_validate_type }, + { &QCfoundry, font_prop_validate_symbol }, + { &QCfamily, font_prop_validate_symbol }, + { &QCadstyle, font_prop_validate_symbol }, + { &QCregistry, font_prop_validate_symbol }, + { &QCweight, font_prop_validate_style }, + { &QCslant, font_prop_validate_style }, + { &QCwidth, font_prop_validate_style }, + { &QCsize, font_prop_validate_size }, + { &QCextra, font_prop_validate_extra } + }; + +static enum font_property_index +check_font_prop_name (key) + Lisp_Object key; +{ + enum font_property_index i; + + for (i = FONT_TYPE_INDEX; i < FONT_SPEC_MAX; i++) + if (EQ (key, *font_property_table[i].key)) + break; + return i; +} + +static Lisp_Object +font_prop_validate (spec) + Lisp_Object spec; +{ + enum font_property_index i; + Lisp_Object val; + + for (i = FONT_TYPE_INDEX; i <= FONT_EXTRA_INDEX; i++) + { + if (! NILP (AREF (spec, i))) + { + val = (font_property_table[i].validater) (i, AREF (spec, i)); + if (EQ (val, Qerror)) + Fsignal (Qerror, list3 (build_string ("invalid font property"), + *font_property_table[i].key, + AREF (spec, i))); + ASET (spec, i, val); + } + } + return spec; +} + + +/* Font name parser and unparser */ + +/* An enumerator for each field of an XLFD font name. */ + +enum xlfd_field_index +{ + XLFD_FOUNDRY_INDEX, + XLFD_FAMILY_INDEX, + XLFD_WEIGHT_INDEX, + XLFD_SLANT_INDEX, + XLFD_SWIDTH_INDEX, + XLFD_ADSTYLE_INDEX, + XLFD_PIXEL_SIZE_INDEX, + XLFD_POINT_SIZE_INDEX, + XLFD_RESX_INDEX, + XLFD_RESY_INDEX, + XLFD_SPACING_INDEX, + XLFD_AVGWIDTH_INDEX, + XLFD_REGISTRY_INDEX, + XLFD_ENCODING_INDEX, + XLFD_LAST_INDEX +}; + +/* Return a symbol interned by string at STR and bytes LEN. + If LEN == 0, return a null string. + If the string is "*", return Qnil. + It is assured that LEN < 256. */ + +static Lisp_Object +intern_font_field (f, xlfd) + char *f[XLFD_LAST_INDEX + 1]; + int xlfd; +{ + char *str = f[xlfd] + 1; + int len; + + if (xlfd != XLFD_RESY_INDEX) + len = f[xlfd + 1] - f[xlfd] - 1; + else + len = f[XLFD_REGISTRY_INDEX] - f[xlfd] - 1; + + if (len == 0) + return null_string; + if (*str == '*' && len == 1) + return Qnil; + return intern_downcase (str, len); +} + +/* Parse P pointing the pixel/point size field of the form + `[A B C D]' which specifies a transformation matrix: + + A B 0 + C D 0 + 0 0 1 + + by which all glyphs of the font are transformed. The spec says + that scalar value N for the pixel/point size is equivalent to: + A = N * resx/resy, B = C = 0, D = N. + + Return the scalar value N if the form is valid. Otherwise return + -1. */ + +static int +parse_matrix (p) + char *p; +{ + double matrix[4]; + char *end; + int i; + + for (i = 0, p++; i < 4 && *p && *p != ']'; i++) + { + if (*p == '~') + matrix[i] = - strtod (p + 1, &end); + else + matrix[i] = strtod (p, &end); + p = end; + } + return (i == 4 ? (int) matrix[3] : -1); +} + +/* Parse NAME (null terminated) as XLFD format, and store information + in FONT (font-spec or font-entity). If NAME is successfully + parsed, return 2 (non-scalable font), 1 (scalable vector font), or + 0 (auto-scaled font). Otherwise return -1. + + If FONT is a font-entity, store RESY-SPACING-AVWIDTH information as + a symbol in FONT_EXTRA_INDEX. + + If MERGE is nonzero, set a property of FONT only when it's nil. */ + +int +font_parse_xlfd (name, font, merge) + char *name; + Lisp_Object font; + int merge; +{ + int len = strlen (name); + int i, j; + int pixel_size, resy, avwidth; + double point_size; + char *f[XLFD_LAST_INDEX + 1]; + Lisp_Object val; + int first_wildcard_field = -1, last_wildcard_field = XLFD_LAST_INDEX; + + if (len > 255) + /* Maximum XLFD name length is 255. */ + return -1; + for (i = 0; *name; name++) + if (*name == '-' + && i < XLFD_LAST_INDEX) + { + f[i] = name; + if (name[1] == '*' && (! name[2] || name[2] == '-')) + { + if (first_wildcard_field < 0) + first_wildcard_field = i; + last_wildcard_field = i; + } + i++; + } + + f[XLFD_LAST_INDEX] = name; + if (i < XLFD_LAST_INDEX) + { + /* Not a fully specified XLFD. */ + if (first_wildcard_field < 0 ) + /* No wild card. */ + return -1; + i--; + if (last_wildcard_field < i) + { + /* Shift fields after the last wildcard field. */ + for (j = XLFD_LAST_INDEX - 1; j > last_wildcard_field; j--, i--) + f[j] = f[i]; + /* Make all fields between the first and last wildcard fieled + also wildcard fields. */ + for (j--; j > first_wildcard_field; j--) + f[j] = "-*"; + } + } + f[XLFD_ENCODING_INDEX] = f[XLFD_LAST_INDEX]; + + if (! merge || NILP (AREF (font, FONT_FOUNDRY_INDEX))) + ASET (font, FONT_FOUNDRY_INDEX, intern_font_field (f, XLFD_FOUNDRY_INDEX)); + if (! merge || NILP (AREF (font, FONT_FAMILY_INDEX))) + ASET (font, FONT_FAMILY_INDEX, intern_font_field (f, XLFD_FAMILY_INDEX)); + if (! merge || NILP (AREF (font, FONT_ADSTYLE_INDEX))) + ASET (font, FONT_ADSTYLE_INDEX, intern_font_field (f, XLFD_ADSTYLE_INDEX)); + if (! merge || NILP (AREF (font, FONT_REGISTRY_INDEX))) + ASET (font, FONT_REGISTRY_INDEX, intern_font_field (f, XLFD_REGISTRY_INDEX)); + + for (i = FONT_WEIGHT_INDEX, j = XLFD_WEIGHT_INDEX; + j <= XLFD_SWIDTH_INDEX; i++, j++) + if (! merge || NILP (AREF (font, i))) + { + if (isdigit(f[j][1])) + val = make_number (atoi (f[j] + 1)); + else + { + Lisp_Object sym = intern_font_field (f, j); + + val = prop_name_to_numeric (i, sym); + if (NILP (val)) + val = sym; + } + ASET (font, i, val); + } + + if (f[XLFD_PIXEL_SIZE_INDEX][1] == '*') + pixel_size = -1; /* indicates "unspecified" */ + else if (f[XLFD_PIXEL_SIZE_INDEX][1] == '[') + pixel_size = parse_matrix (f[XLFD_PIXEL_SIZE_INDEX] + 1); + else if (isdigit (f[XLFD_PIXEL_SIZE_INDEX][1])) + pixel_size = strtod (f[XLFD_PIXEL_SIZE_INDEX] + 1, NULL); + else + pixel_size = -1; + + if (pixel_size < 0 && FONT_ENTITY_P (font)) + return -1; + + if (f[XLFD_POINT_SIZE_INDEX][1] == '*') + point_size = -1; /* indicates "unspecified" */ + else if (f[XLFD_POINT_SIZE_INDEX][1] == '[') + point_size = parse_matrix (f[XLFD_POINT_SIZE_INDEX] + 1); + else if (isdigit (f[XLFD_POINT_SIZE_INDEX][1])) + point_size = strtod (f[XLFD_POINT_SIZE_INDEX] + 1, NULL); + else + point_size = -1; + + if (f[XLFD_RESY_INDEX][1] == '*') + resy = -1; /* indicates "unspecified" */ + else + resy = strtod (f[XLFD_RESY_INDEX] + 1, NULL); + + if (f[XLFD_AVGWIDTH_INDEX][1] == '*') + avwidth = -1; /* indicates "unspecified" */ + else if (f[XLFD_AVGWIDTH_INDEX][1] == '~') + avwidth = - strtod (f[XLFD_AVGWIDTH_INDEX] + 2, NULL); + else + avwidth = strtod (f[XLFD_AVGWIDTH_INDEX] + 1, NULL); + + if (! merge || NILP (AREF (font, FONT_SIZE_INDEX))) + { + if (pixel_size >= 0) + ASET (font, FONT_SIZE_INDEX, make_number (pixel_size)); + else + { + if (point_size >= 0) + { + if (resy > 0) + { + pixel_size = POINT_TO_PIXEL (point_size, resy); + ASET (font, FONT_SIZE_INDEX, make_number (pixel_size)); + } + else + { + ASET (font, FONT_SIZE_INDEX, make_float (point_size / 10)); + } + } + else + ASET (font, FONT_SIZE_INDEX, Qnil); + } + } + + if (FONT_ENTITY_P (font) + && EQ (AREF (font, FONT_TYPE_INDEX), Qx)) + ASET (font, FONT_EXTRA_INDEX, intern_font_field (f, XLFD_RESY_INDEX)); + + return (avwidth > 0 ? 2 : resy == 0); +} + +/* Store XLFD name of FONT (font-spec or font-entity) in NAME (NBYTES + length), and return the name length. If FONT_SIZE_INDEX of FONT is + 0, use PIXEL_SIZE instead. */ + +int +font_unparse_xlfd (font, pixel_size, name, nbytes) + Lisp_Object font; + char *name; + int nbytes; +{ + char *f[XLFD_REGISTRY_INDEX + 1], *pixel_point; + char work[256]; + Lisp_Object val; + int i, j, len = 0; + + xassert (FONTP (font)); + + for (i = FONT_FOUNDRY_INDEX, j = XLFD_FOUNDRY_INDEX; i <= FONT_REGISTRY_INDEX; + i++, j++) + { + if (i == FONT_ADSTYLE_INDEX) + j = XLFD_ADSTYLE_INDEX; + else if (i == FONT_REGISTRY_INDEX) + j = XLFD_REGISTRY_INDEX; + val = AREF (font, i); + if (NILP (val)) + f[j] = "*", len += 2; + else + { + if (SYMBOLP (val)) + val = SYMBOL_NAME (val); + f[j] = (char *) SDATA (val), len += SBYTES (val) + 1; + } + } + + for (i = FONT_WEIGHT_INDEX, j = XLFD_WEIGHT_INDEX; i <= FONT_WIDTH_INDEX; + i++, j++) + { + val = AREF (font, i); + if (NILP (val)) + f[j] = "*", len += 2; + else + { + if (INTEGERP (val)) + val = prop_numeric_to_name (i, XINT (val)); + if (SYMBOLP (val)) + val = SYMBOL_NAME (val); + xassert (STRINGP (val)); + f[j] = (char *) SDATA (val), len += SBYTES (val) + 1; + } + } + + val = AREF (font, FONT_SIZE_INDEX); + xassert (NUMBERP (val) || NILP (val)); + if (INTEGERP (val)) + { + i = XINT (val); + if (i > 0) + len += sprintf (work, "%d", i) + 1; + else /* i == 0 */ + len += sprintf (work, "%d-*", pixel_size) + 1; + pixel_point = work; + } + else if (FLOATP (val)) + { + i = XFLOAT_DATA (val) * 10; + len += sprintf (work, "*-%d", i) + 1; + pixel_point = work; + } + else + pixel_point = "*-*", len += 4; + + if (FONT_ENTITY_P (font) + && EQ (AREF (font, FONT_TYPE_INDEX), Qx)) + { + /* Setup names for RESY-SPACING-AVWIDTH. */ + val = AREF (font, FONT_EXTRA_INDEX); + if (SYMBOLP (val) && ! NILP (val)) + { + val = SYMBOL_NAME (val); + f[XLFD_RESY_INDEX] = (char *) SDATA (val), len += SBYTES (val) + 1; + } + else + f[XLFD_RESY_INDEX] = "*-*-*", len += 6; + } + else + f[XLFD_RESY_INDEX] = "*-*-*", len += 6; + + len += 3; /* for "-*" of resx, and terminating '\0'. */ + if (len >= nbytes) + return -1; + return sprintf (name, "-%s-%s-%s-%s-%s-%s-%s-*-%s-%s", + f[XLFD_FOUNDRY_INDEX], f[XLFD_FAMILY_INDEX], + f[XLFD_WEIGHT_INDEX], f[XLFD_SLANT_INDEX], + f[XLFD_SWIDTH_INDEX], + f[XLFD_ADSTYLE_INDEX], pixel_point, + f[XLFD_RESY_INDEX], f[XLFD_REGISTRY_INDEX]); +} + +void +font_merge_old_spec (name, family, registry, spec) + Lisp_Object name, family, registry, spec; +{ + if (STRINGP (name)) + { + if (font_parse_xlfd ((char *) SDATA (name), spec, 1) < 0) + { + Lisp_Object extra = Fcons (Fcons (QCname, name), Qnil); + + ASET (spec, FONT_EXTRA_INDEX, extra); + } + } + else + { + if (! NILP (family)) + { + int len; + char *p0, *p1; + + xassert (STRINGP (family)); + len = SBYTES (family); + p0 = (char *) SDATA (family); + p1 = index (p0, '-'); + if (p1) + { + if (NILP (AREF (spec, FONT_FOUNDRY_INDEX))) + ASET (spec, FONT_FOUNDRY_INDEX, + intern_downcase (p0, p1 - p0)); + if (NILP (AREF (spec, FONT_FAMILY_INDEX))) + ASET (spec, FONT_FAMILY_INDEX, + intern_downcase (p1 + 1, len - (p1 + 1 - p0))); + } + else if (NILP (AREF (spec, FONT_FAMILY_INDEX))) + ASET (spec, FONT_FAMILY_INDEX, intern_downcase (p0, len)); + } + if (! NILP (registry) + && NILP (AREF (spec, FONT_REGISTRY_INDEX))) + ASET (spec, FONT_REGISTRY_INDEX, + intern_downcase ((char *) SDATA (registry), SBYTES (registry))); + } +} + + +/* OTF handler */ + +#ifdef HAVE_LIBOTF +#include + +struct otf_list +{ + Lisp_Object entity; + OTF *otf; + struct otf_list *next; +}; + +static struct otf_list *otf_list; + +static Lisp_Object +otf_tag_symbol (tag) + OTF_Tag tag; +{ + char name[5]; + + OTF_tag_name (tag, name); + return Fintern (make_unibyte_string (name, 4), Qnil); +} + +static OTF * +otf_open (entity, file) + Lisp_Object entity; + char *file; +{ + struct otf_list *list = otf_list; + + while (list && ! EQ (list->entity, entity)) + list = list->next; + if (! list) + { + list = malloc (sizeof (struct otf_list)); + list->entity = entity; + list->otf = file ? OTF_open (file) : NULL; + list->next = otf_list; + otf_list = list; + } + return list->otf; +} + + +/* Return a list describing which scripts/languages FONT supports by + which GSUB/GPOS features of OpenType tables. See the comment of + (sturct font_driver).otf_capability. */ + +Lisp_Object +font_otf_capability (font) + struct font *font; +{ + OTF *otf; + Lisp_Object capability = Fcons (Qnil, Qnil); + int i; + + otf = otf_open (font->entity, font->file_name); + if (! otf) + return Qnil; + for (i = 0; i < 2; i++) + { + OTF_GSUB_GPOS *gsub_gpos; + Lisp_Object script_list = Qnil; + int j; + + if (OTF_get_features (otf, i == 0) < 0) + continue; + gsub_gpos = i == 0 ? otf->gsub : otf->gpos; + for (j = gsub_gpos->ScriptList.ScriptCount - 1; j >= 0; j--) + { + OTF_Script *script = gsub_gpos->ScriptList.Script + j; + Lisp_Object langsys_list = Qnil; + Lisp_Object script_tag = otf_tag_symbol (script->ScriptTag); + int k; + + for (k = script->LangSysCount; k >= 0; k--) + { + OTF_LangSys *langsys; + Lisp_Object feature_list = Qnil; + Lisp_Object langsys_tag; + int l; + + if (j == script->LangSysCount) + { + langsys = &script->DefaultLangSys; + langsys_tag = Qnil; + } + else + { + langsys = script->LangSys + k; + langsys_tag + = otf_tag_symbol (script->LangSysRecord[k].LangSysTag); + } + for (l = langsys->FeatureCount -1; l >= 0; l--) + { + OTF_Feature *feature + = gsub_gpos->FeatureList.Feature + langsys->FeatureIndex[l]; + Lisp_Object feature_tag + = otf_tag_symbol (feature->FeatureTag); + + feature_list = Fcons (feature_tag, feature_list); + } + langsys_list = Fcons (Fcons (langsys_tag, feature_list), + langsys_list); + } + script_list = Fcons (Fcons (script_tag, langsys_list), + script_list); + } + + if (i == 0) + XSETCAR (capability, script_list); + else + XSETCDR (capability, script_list); + } + + return capability; +} + +static int +parse_gsub_gpos_spec (spec, script, langsys, features) + Lisp_Object spec; + char **script, **langsys, **features; +{ + Lisp_Object val; + int len; + char *p; + int asterisk; + + val = XCAR (spec); + *script = (char *) SDATA (SYMBOL_NAME (val)); + spec = XCDR (spec); + val = XCAR (spec); + *langsys = NILP (val) ? NULL : (char *) SDATA (SYMBOL_NAME (val)); + spec = XCDR (spec); + len = XINT (Flength (spec)); + *features = p = malloc (6 * len); + if (! p) + return -1; + + for (asterisk = 0; CONSP (spec); spec = XCDR (spec)) + { + val = XCAR (spec); + if (SREF (SYMBOL_NAME (val), 0) == '*') + { + asterisk = 1; + p += sprintf (p, ",*"); + } + else if (! asterisk) + p += sprintf (p, ",%s", SDATA (SYMBOL_NAME (val))); + else + p += sprintf (p, ",~%s", SDATA (SYMBOL_NAME (val))); + } + return 0; +} + +#define DEVICE_DELTA(table, size) \ + (((size) >= (table).StartSize && (size) <= (table).EndSize) \ + ? (table).DeltaValue[(size) >= (table).StartSize] \ + : 0) + +void +adjust_anchor (struct font *font, OTF_Anchor *anchor, + unsigned code, int size, int *x, int *y) +{ + if (anchor->AnchorFormat == 2) + { + int x0, y0; + + if (font->driver->anchor_point (font, code, anchor->f.f1.AnchorPoint, + &x0, &y0) >= 0) + *x = x0, *y = y0; + } + else if (anchor->AnchorFormat == 3) + { + if (anchor->f.f2.XDeviceTable.offset) + *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, size); + if (anchor->f.f2.YDeviceTable.offset) + *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, size); + } +} + + +/* Drive FONT's OTF GSUB features according to GSUB_SPEC. See the + comment of (sturct font_driver).otf_gsub. */ + +int +font_otf_gsub (font, gsub_spec, gstring_in, from, to, gstring_out, idx) + struct font *font; + Lisp_Object gsub_spec; + Lisp_Object gstring_in; + int from, to; + Lisp_Object gstring_out; + int idx; +{ + int len; + int i; + OTF *otf; + OTF_GlyphString otf_gstring; + OTF_Glyph *g; + char *script, *langsys, *features; + + otf = otf_open (font->entity, font->file_name); + if (! otf) + return 0; + if (OTF_get_table (otf, "head") < 0) + return 0; + if (OTF_check_table (otf, "GSUB") < 0) + return 0; + if (parse_gsub_gpos_spec (gsub_spec, &script, &langsys, &features) < 0) + return 0; + len = to - from; + otf_gstring.size = otf_gstring.used = len; + otf_gstring.glyphs = (OTF_Glyph *) malloc (sizeof (OTF_Glyph) * len); + memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * len); + for (i = 0; i < len; i++) + { + Lisp_Object g = LGSTRING_GLYPH (gstring_in, from + i); + + otf_gstring.glyphs[i].c = XINT (LGLYPH_CHAR (g)); + otf_gstring.glyphs[i].glyph_id = XINT (LGLYPH_CODE (g)); + } + + OTF_drive_gdef (otf, &otf_gstring); + if (OTF_drive_gsub (otf, &otf_gstring, script, langsys, features) < 0) + { + free (otf_gstring.glyphs); + return 0; + } + if (ASIZE (gstring_out) < idx + otf_gstring.used) + { + free (otf_gstring.glyphs); + return -1; + } + + for (i = 0, g = otf_gstring.glyphs; i < otf_gstring.used;) + { + int i0 = g->f.index.from, i1 = g->f.index.to; + Lisp_Object glyph = LGSTRING_GLYPH (gstring_in, from + i0); + Lisp_Object min_idx = AREF (glyph, 0); + Lisp_Object max_idx = AREF (glyph, 1); + + if (i0 < i1) + { + int min_idx_i = XINT (min_idx), max_idx_i = XINT (max_idx); + + for (i0++; i0 <= i1; i0++) + { + glyph = LGSTRING_GLYPH (gstring_in, from + i0); + if (min_idx_i > XINT (AREF (glyph, 0))) + min_idx_i = XINT (AREF (glyph, 0)); + if (max_idx_i < XINT (AREF (glyph, 1))) + max_idx_i = XINT (AREF (glyph, 1)); + } + min_idx = make_number (min_idx_i); + max_idx = make_number (max_idx_i); + i0 = g->f.index.from; + } + for (; i < otf_gstring.used && g->f.index.from == i0; i++, g++) + { + glyph = LGSTRING_GLYPH (gstring_out, idx + i); + ASET (glyph, 0, min_idx); + ASET (glyph, 1, max_idx); + LGLYPH_SET_CHAR (glyph, make_number (g->c)); + LGLYPH_SET_CODE (glyph, make_number (g->glyph_id)); + } + } + + free (otf_gstring.glyphs); + return i; +} + +/* Drive FONT's OTF GPOS features according to GPOS_SPEC. See the + comment of (sturct font_driver).otf_gpos. */ + +int +font_otf_gpos (font, gpos_spec, gstring, from, to) + struct font *font; + Lisp_Object gpos_spec; + Lisp_Object gstring; + int from, to; +{ + int len; + int i; + OTF *otf; + OTF_GlyphString otf_gstring; + OTF_Glyph *g; + char *script, *langsys, *features; + Lisp_Object glyph; + int u, size; + Lisp_Object base, mark; + + otf = otf_open (font->entity, font->file_name); + if (! otf) + return 0; + if (OTF_get_table (otf, "head") < 0) + return 0; + if (OTF_check_table (otf, "GPOS") < 0) + return 0; + if (parse_gsub_gpos_spec (gpos_spec, &script, &langsys, &features) < 0) + return 0; + len = to - from; + otf_gstring.size = otf_gstring.used = len; + otf_gstring.glyphs = (OTF_Glyph *) malloc (sizeof (OTF_Glyph) * len); + memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * len); + for (i = 0; i < len; i++) + { + glyph = LGSTRING_GLYPH (gstring, from + i); + otf_gstring.glyphs[i].glyph_id = XINT (LGLYPH_CODE (glyph)); + } + + OTF_drive_gdef (otf, &otf_gstring); + + if (OTF_drive_gpos (otf, &otf_gstring, script, langsys, features) < 0) + { + free (otf_gstring.glyphs); + return 0; + } + + u = otf->head->unitsPerEm; + size = font->pixel_size; + base = mark = Qnil; + for (i = 0, g = otf_gstring.glyphs; i < otf_gstring.used; i++, g++) + { + Lisp_Object prev; + int xoff = 0, yoff = 0, width_adjust = 0; + + if (! g->glyph_id) + continue; + + glyph = LGSTRING_GLYPH (gstring, from + i); + switch (g->positioning_type) + { + case 0: + break; + case 1: case 2: + { + int format = g->f.f1.format; + + if (format & OTF_XPlacement) + xoff = g->f.f1.value->XPlacement * size / u; + if (format & OTF_XPlaDevice) + xoff += DEVICE_DELTA (g->f.f1.value->XPlaDevice, size); + if (format & OTF_YPlacement) + yoff = - (g->f.f1.value->YPlacement * size / u); + if (format & OTF_YPlaDevice) + yoff -= DEVICE_DELTA (g->f.f1.value->YPlaDevice, size); + if (format & OTF_XAdvance) + width_adjust += g->f.f1.value->XAdvance * size / u; + if (format & OTF_XAdvDevice) + width_adjust += DEVICE_DELTA (g->f.f1.value->XAdvDevice, size); + } + break; + case 3: + /* Not yet supported. */ + break; + case 4: case 5: + if (NILP (base)) + break; + prev = base; + goto label_adjust_anchor; + default: /* i.e. case 6 */ + if (NILP (mark)) + break; + prev = mark; + + label_adjust_anchor: + { + int base_x, base_y, mark_x, mark_y, width; + unsigned code; + + base_x = g->f.f4.base_anchor->XCoordinate * size / u; + base_y = g->f.f4.base_anchor->YCoordinate * size / u; + mark_x = g->f.f4.mark_anchor->XCoordinate * size / u; + mark_y = g->f.f4.mark_anchor->YCoordinate * size / u; + + code = XINT (LGLYPH_CODE (prev)); + if (g->f.f4.base_anchor->AnchorFormat != 1) + adjust_anchor (font, g->f.f4.base_anchor, + code, size, &base_x, &base_y); + if (g->f.f4.mark_anchor->AnchorFormat != 1) + adjust_anchor (font, g->f.f4.mark_anchor, + code, size, &mark_x, &mark_y); + + if (NILP (LGLYPH_WIDTH (prev))) + { + width = font->driver->text_extents (font, &code, 1, NULL); + LGLYPH_SET_WIDTH (prev, make_number (width)); + } + xoff = XINT (LGLYPH_XOFF (prev)) + (base_x - width) - mark_x; + yoff = XINT (LGLYPH_YOFF (prev)) + mark_y - base_y; + } + } + if (g->GlyphClass == OTF_GlyphClass0) + base = mark = glyph; + else if (g->GlyphClass == OTF_GlyphClassMark) + mark = glyph; + else + base = glyph; + + LGLYPH_SET_XOFF (glyph, make_number (xoff)); + LGLYPH_SET_YOFF (glyph, make_number (yoff)); + LGLYPH_SET_WADJUST (glyph, make_number (width_adjust)); + } + + free (otf_gstring.glyphs); + return 0; +} + +#endif /* HAVE_LIBOTF */ + + +/* glyph-string handler */ + +/* GSTRING is a vector of this form: + [ [FONT-OBJECT LBEARING RBEARING WITH ASCENT DESCENT] GLYPH ... ] + and GLYPH is a vector of this form: + [ FROM-IDX TO-IDX C CODE X-OFF Y-OFF WIDTH WADJUST ] + where + FROM-IDX and TO-IDX are used internally and should not be touched. + C is a character of the glyph. + CODE is a glyph-code of C in FONT-OBJECT. + X-OFF and Y-OFF are offests to the base position for the glyph. + WIDTH is a normal width of the glyph. + WADJUST is an adjustment to the normal width of the glyph. */ + +struct font * +font_prepare_composition (cmp) + struct composition *cmp; +{ + Lisp_Object gstring + = AREF (XHASH_TABLE (composition_hash_table)->key_and_value, + cmp->hash_index * 2); + struct font *font = XSAVE_VALUE (LGSTRING_FONT (gstring))->pointer; + int len = LGSTRING_LENGTH (gstring); + int i; + + cmp->font = font; + cmp->lbearing = cmp->rbearing = cmp->pixel_width = 0; + cmp->ascent = font->ascent; + cmp->descent = font->descent; + + for (i = 0; i < len; i++) + { + Lisp_Object g = LGSTRING_GLYPH (gstring, i); + unsigned code = XINT (LGLYPH_CODE (g)); + struct font_metrics metrics; + + font->driver->text_extents (font, &code, 1, &metrics); + LGLYPH_SET_WIDTH (g, make_number (metrics.width)); + metrics.lbearing += XINT (LGLYPH_XOFF (g)); + metrics.rbearing += XINT (LGLYPH_XOFF (g)); + metrics.ascent += XINT (LGLYPH_YOFF (g)); + metrics.descent += XINT (LGLYPH_YOFF (g)); + + if (cmp->lbearing > cmp->pixel_width + metrics.lbearing) + cmp->lbearing = cmp->pixel_width + metrics.lbearing; + if (cmp->rbearing < cmp->pixel_width + metrics.rbearing) + cmp->rbearing = cmp->pixel_width + metrics.rbearing; + if (cmp->ascent < metrics.ascent) + cmp->ascent = metrics.ascent; + if (cmp->descent < metrics.descent) + cmp->descent = metrics.descent; + cmp->pixel_width += metrics.width + XINT (LGLYPH_WADJUST (g)); + } + LGSTRING_SET_LBEARING (gstring, make_number (cmp->lbearing)); + LGSTRING_SET_RBEARING (gstring, make_number (cmp->rbearing)); + LGSTRING_SET_WIDTH (gstring, make_number (cmp->pixel_width)); + LGSTRING_SET_ASCENT (gstring, make_number (cmp->ascent)); + LGSTRING_SET_DESCENT (gstring, make_number (cmp->descent)); + + return font; +} + +int +font_gstring_produce (old, from, to, new, idx, code, n) + Lisp_Object old; + int from, to; + Lisp_Object new; + int idx; + unsigned *code; + int n; +{ + Lisp_Object min_idx, max_idx; + int i; + + if (idx + n > ASIZE (new)) + return -1; + if (from == to) + { + if (from == 0) + { + min_idx = make_number (0); + max_idx = make_number (1); + } + else + { + min_idx = AREF (AREF (old, from - 1), 0); + max_idx = AREF (AREF (old, from - 1), 1); + } + } + else if (from + 1 == to) + { + min_idx = AREF (AREF (old, from), 0); + max_idx = AREF (AREF (old, from), 1); + } + else + { + int min_idx_i = XINT (AREF (AREF (old, from), 0)); + int max_idx_i = XINT (AREF (AREF (old, from), 1)); + + for (i = from + 1; i < to; i++) + { + if (min_idx_i > XINT (AREF (AREF (old, i), 0))) + min_idx_i = XINT (AREF (AREF (old, i), 0)); + if (max_idx_i < XINT (AREF (AREF (old, i), 1))) + max_idx_i = XINT (AREF (AREF (old, i), 1)); + } + min_idx = make_number (min_idx_i); + max_idx = make_number (max_idx_i); + } + + for (i = 0; i < n; i++) + { + ASET (AREF (new, idx + i), 0, min_idx); + ASET (AREF (new, idx + i), 1, max_idx); + ASET (AREF (new, idx + i), 2, make_number (code[i])); + } + + return 0; +} + +/* Font sorting */ + +static unsigned font_score P_ ((Lisp_Object, Lisp_Object)); +static int font_compare P_ ((const void *, const void *)); +static Lisp_Object font_sort_entites P_ ((Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object)); + +/* We sort fonts by scoring each of them against a specified + font-spec. The score value is 32 bit (`unsigned'), and the smaller + the value is, the closer the font is to the font-spec. + + Each 1-bit in the highest 4 bits of the score is used for atomic + properties FOUNDRY, FAMILY, ADSTYLE, and REGISTRY. + + Each 7-bit in the lowest 28 bits are used for numeric properties + WEIGHT, SLANT, WIDTH, and SIZE. */ + +/* How many bits to shift to store the difference value of each font + property in a score. */ +static int sort_shift_bits[FONT_SIZE_INDEX + 1]; + +/* Score font-entity ENTITY against font-spec SPEC. The return value + indicates how different ENTITY is compared with SPEC. */ + +static unsigned +font_score (entity, spec) + Lisp_Object entity, spec; +{ + unsigned score = 0; + int i; + /* Score atomic fields. Maximum difference is 1. */ + for (i = FONT_FOUNDRY_INDEX; i <= FONT_REGISTRY_INDEX; i++) + { + Lisp_Object val = AREF (spec, i); + + if (! NILP (val) + && ! EQ (val, AREF (entity, i))) + score |= 1 << sort_shift_bits[i]; + } + + /* Score numeric fields. Maximum difference is 127. */ + for (i = FONT_WEIGHT_INDEX; i <= FONT_SIZE_INDEX; i++) + { + Lisp_Object spec_val = AREF (spec, i); + Lisp_Object entity_val = AREF (entity, i); + + if (! NILP (spec_val) && ! EQ (spec_val, entity_val)) + { + if (! INTEGERP (entity_val)) + score |= 127 << sort_shift_bits[i]; + else if (i < FONT_SIZE_INDEX + || XINT (entity_val) != 0) + { + int diff = XINT (entity_val) - XINT (spec_val); + + if (diff < 0) + diff = - diff; + score |= min (diff, 127) << sort_shift_bits[i]; + } + } + } + + return score; +} + + +/* The comparison function for qsort. */ + +static int +font_compare (d1, d2) + const void *d1, *d2; +{ + return (*(unsigned *) d1 < *(unsigned *) d2 + ? -1 : *(unsigned *) d1 > *(unsigned *) d2); +} + + +/* The structure for elements being sorted by qsort. */ +struct font_sort_data +{ + unsigned score; + Lisp_Object entity; +}; + + +/* Sort font-entities in vector VEC by closeness to font-spec PREFER. + If PREFER specifies a point-size, calculate the corresponding + pixel-size from the Y-resolution of FRAME before sorting. If SPEC + is not nil, it is a font-spec to get the font-entities in VEC. */ + +static Lisp_Object +font_sort_entites (vec, prefer, frame, spec) + Lisp_Object vec, prefer, frame, spec; +{ + Lisp_Object size; + int len, i; + struct font_sort_data *data; + int prefer_is_copy = 0; + USE_SAFE_ALLOCA; + + len = ASIZE (vec); + if (len <= 1) + return vec; + + size = AREF (spec, FONT_SIZE_INDEX); + if (FLOATP (size)) + { + double point_size = XFLOAT_DATA (size) * 10; + int pixel_size = POINT_TO_PIXEL (point_size, XFRAME (frame)->resy); + + prefer = Fcopy_sequence (prefer); + ASET (prefer, FONT_SIZE_INDEX, make_number (pixel_size)); + prefer_is_copy = 1; + } + + if (! NILP (spec)) + { + /* As it is assured that all fonts in VEC match with SPEC, we + should ignore properties specified in SPEC. So, set the + corresponding properties in PREFER nil. */ + for (i = FONT_WEIGHT_INDEX; i <= FONT_SIZE_INDEX; i++) + if (! NILP (AREF (spec, i)) && ! NILP (AREF (prefer, i))) + break; + if (i <= FONT_SIZE_INDEX) + { + if (! prefer_is_copy) + prefer = Fcopy_sequence (prefer); + for (; i <= FONT_SIZE_INDEX; i++) + if (! NILP (AREF (spec, i)) && ! NILP (AREF (prefer, i))) + ASET (prefer, i, Qnil); + } + } + + /* Scoring and sorting. */ + SAFE_ALLOCA (data, struct font_sort_data *, (sizeof *data) * len); + for (i = 0; i < len; i++) + { + data[i].entity = AREF (vec, i); + data[i].score = font_score (data[i].entity, prefer); + } + qsort (data, len, sizeof *data, font_compare); + for (i = 0; i < len; i++) + ASET (vec, i, data[i].entity); + SAFE_FREE (); + + return vec; +} + + +/* API of Font Service Layer. */ + +void +font_update_sort_order (order) + int *order; +{ + int i, shift_bits = 21; + + for (i = 0; i < 4; i++, shift_bits -= 7) + { + int xlfd_idx = order[i]; + + if (xlfd_idx == XLFD_WEIGHT_INDEX) + sort_shift_bits[FONT_WEIGHT_INDEX] = shift_bits; + else if (xlfd_idx == XLFD_SLANT_INDEX) + sort_shift_bits[FONT_SLANT_INDEX] = shift_bits; + else if (xlfd_idx == XLFD_SWIDTH_INDEX) + sort_shift_bits[FONT_WIDTH_INDEX] = shift_bits; + else + sort_shift_bits[FONT_SIZE_INDEX] = shift_bits; + } +} + +Lisp_Object +font_symbolic_weight (font) + Lisp_Object font; +{ + Lisp_Object weight = AREF (font, FONT_WEIGHT_INDEX); + + if (INTEGERP (weight)) + weight = prop_numeric_to_name (FONT_WEIGHT_INDEX, XINT (weight)); + return weight; +} + +Lisp_Object +font_symbolic_slant (font) + Lisp_Object font; +{ + Lisp_Object slant = AREF (font, FONT_SLANT_INDEX); + + if (INTEGERP (slant)) + slant = prop_numeric_to_name (FONT_SLANT_INDEX, XINT (slant)); + return slant; +} + +Lisp_Object +font_symbolic_width (font) + Lisp_Object font; +{ + Lisp_Object width = AREF (font, FONT_WIDTH_INDEX); + + if (INTEGERP (width)) + width = prop_numeric_to_name (FONT_WIDTH_INDEX, XINT (width)); + return width; +} + +Lisp_Object +font_find_object (font) + struct font *font; +{ + Lisp_Object tail, elt; + + for (tail = AREF (font->entity, FONT_OBJLIST_INDEX); CONSP (tail); + tail = XCDR (tail)) + { + elt = XCAR (tail); + if (font == XSAVE_VALUE (elt)->pointer + && XSAVE_VALUE (elt)->integer > 0) + return elt; + } + abort (); + return Qnil; +} + +static Lisp_Object scratch_font_spec, scratch_font_prefer; + +/* Return a vector of font-entities matching with SPEC on frame F. */ + +static Lisp_Object +font_list_entities (frame, spec) + Lisp_Object frame, spec; +{ + FRAME_PTR f = XFRAME (frame); + struct font_driver_list *driver_list = f->font_driver_list; + Lisp_Object ftype, family, alternate_familes; + Lisp_Object *vec = alloca (sizeof (Lisp_Object) * num_font_drivers); + int i; + + if (! vec) + return null_vector; + + family = AREF (spec, FONT_FAMILY_INDEX); + if (NILP (family)) + alternate_familes = Qnil; + else + { + if (NILP (font_family_alist) + && !NILP (Vface_alternative_font_family_alist)) + build_font_family_alist (); + alternate_familes = assq_no_quit (family, font_family_alist); + if (! NILP (alternate_familes)) + alternate_familes = XCDR (alternate_familes); + } + xassert (ASIZE (spec) == FONT_SPEC_MAX); + ftype = AREF (spec, FONT_TYPE_INDEX); + + for (i = 0; driver_list; driver_list = driver_list->next) + if (NILP (ftype) || EQ (driver_list->driver->type, ftype)) + { + Lisp_Object cache = driver_list->driver->get_cache (frame); + Lisp_Object tail = alternate_familes; + Lisp_Object val; + + xassert (CONSP (cache)); + ASET (spec, FONT_TYPE_INDEX, driver_list->driver->type); + ASET (spec, FONT_FAMILY_INDEX, family); + + while (1) + { + val = assoc_no_quit (spec, XCDR (cache)); + if (CONSP (val)) + val = XCDR (val); + else + { + val = driver_list->driver->list (frame, spec); + if (VECTORP (val)) + XSETCDR (cache, Fcons (Fcons (Fcopy_sequence (spec), val), + XCDR (cache))); + } + if (VECTORP (val) && ASIZE (val) > 0) + { + vec[i++] = val; + break; + } + if (NILP (tail)) + break; + ASET (spec, FONT_FAMILY_INDEX, XCAR (tail)); + tail = XCDR (tail); + } + } + ASET (spec, FONT_TYPE_INDEX, ftype); + ASET (spec, FONT_FAMILY_INDEX, family); + return (i > 0 ? Fvconcat (i, vec) : null_vector); +} + +static int num_fonts; + +static Lisp_Object +font_open_entity (f, entity, pixel_size) + FRAME_PTR f; + Lisp_Object entity; + int pixel_size; +{ + struct font_driver_list *driver_list; + Lisp_Object objlist, size, val; + struct font *font; + + size = AREF (entity, FONT_SIZE_INDEX); + xassert (NATNUMP (size)); + if (XINT (size) != 0) + pixel_size = XINT (size); + + for (objlist = AREF (entity, FONT_OBJLIST_INDEX); CONSP (objlist); + objlist = XCDR (objlist)) + { + font = XSAVE_VALUE (XCAR (objlist))->pointer; + if (font->pixel_size == pixel_size) + { + XSAVE_VALUE (XCAR (objlist))->integer++; + return XCAR (objlist); + } + } + + xassert (FONT_ENTITY_P (entity)); + val = AREF (entity, FONT_TYPE_INDEX); + for (driver_list = f->font_driver_list; + driver_list && ! EQ (driver_list->driver->type, val); + driver_list = driver_list->next); + if (! driver_list) + return Qnil; + + font = driver_list->driver->open (f, entity, pixel_size); + if (! font) + return Qnil; + val = make_save_value (font, 1); + ASET (entity, FONT_OBJLIST_INDEX, + Fcons (val, AREF (entity, FONT_OBJLIST_INDEX))); + num_fonts++; + return val; +} + +void +font_close_object (f, font_object) + FRAME_PTR f; + Lisp_Object font_object; +{ + struct font *font; + Lisp_Object objlist = AREF (font->entity, FONT_OBJLIST_INDEX); + Lisp_Object tail, prev = Qnil; + + for (prev = Qnil, tail = objlist; CONSP (tail); + prev = tail, tail = XCDR (tail)) + if (EQ (font_object, XCAR (tail))) + { + struct Lisp_Save_Value *p = XSAVE_VALUE (font_object); + + xassert (p->integer > 0); + p->integer--; + if (p->integer == 0) + { + if (font->driver->close) + font->driver->close (f, p->pointer); + p->pointer = NULL; + if (NILP (prev)) + ASET (font->entity, FONT_OBJLIST_INDEX, XCDR (objlist)); + else + XSETCDR (prev, XCDR (objlist)); + } + break; + } +} + +int +font_has_char (f, font_entity, c) + FRAME_PTR f; + Lisp_Object font_entity; + int c; +{ + Lisp_Object type = AREF (font_entity, FONT_TYPE_INDEX); + struct font_driver_list *driver_list; + + for (driver_list = f->font_driver_list; + driver_list && ! EQ (driver_list->driver->type, type); + driver_list = driver_list->next); + if (! driver_list) + return -1; + return driver_list->driver->has_char (font_entity, c); +} + +unsigned +font_encode_char (font_object, c) + Lisp_Object font_object; + int c; +{ + struct font *font = XSAVE_VALUE (font_object)->pointer; + + return font->driver->encode_char (font, c); +} + +char * +font_get_name (font_object) + Lisp_Object font_object; +{ + struct font *font = XSAVE_VALUE (font_object)->pointer; + + return (font->font.full_name ? font->font.full_name + : font->file_name ? font->file_name + : ""); +} + +Lisp_Object +font_get_frame (font) + Lisp_Object font; +{ + if (FONT_OBJECT_P (font)) + font = ((struct font *) XSAVE_VALUE (font)->pointer)->entity; + xassert (FONT_ENTITY_P (font)); + return AREF (font, FONT_FRAME_INDEX); +} + +extern Lisp_Object Qunspecified, Qignore_defface; + +Lisp_Object +font_find_for_lface (f, lface, spec) + FRAME_PTR f; + Lisp_Object *lface; + Lisp_Object spec; +{ + Lisp_Object attrs[LFACE_SLANT_INDEX + 1]; + Lisp_Object frame, val, entities; + int i; + unsigned char try_unspecified[FONT_SPEC_MAX]; + + for (i = 0; i <= LFACE_SLANT_INDEX; i++) + { + val = lface[i]; + if (EQ (val, Qunspecified) || EQ (val, Qignore_defface)) + val = Qnil; + attrs[i] = val; + } + if (NILP (spec)) + for (i = 0; i < FONT_SPEC_MAX; i++) + ASET (scratch_font_spec, i, Qnil); + else + for (i = 0; i < FONT_SPEC_MAX; i++) + ASET (scratch_font_spec, i, AREF (spec, i)); + + /* If SPEC doesn't specify a specific property, it can be tried with + nil even if FACE specifies it. */ + for (i = FONT_FOUNDRY_INDEX; i <= FONT_SIZE_INDEX; i++) + try_unspecified[i] = NILP (AREF (scratch_font_spec, i)); + + if (STRINGP (attrs[LFACE_FONT_INDEX])) + font_merge_old_spec (attrs[LFACE_FONT_INDEX], Qnil, Qnil, + scratch_font_spec); + if (NILP (AREF (scratch_font_spec, FONT_FAMILY_INDEX)) + && ! NILP (attrs[LFACE_FAMILY_INDEX])) + font_merge_old_spec (Qnil, attrs[LFACE_FAMILY_INDEX], Qnil, + scratch_font_spec); + if (NILP (AREF (scratch_font_spec, FONT_REGISTRY_INDEX))) + { + ASET (scratch_font_spec, FONT_REGISTRY_INDEX, intern ("iso8859-1")); + try_unspecified[FONT_REGISTRY_INDEX] = 0; + } + + for (i = FONT_FAMILY_INDEX; i <= FONT_SIZE_INDEX; i++) + if (try_unspecified[i] + && NILP (AREF (scratch_font_spec, i))) + try_unspecified[i] = 0; + + XSETFRAME (frame, f); + entities = font_list_entities (frame, scratch_font_spec); + while (ASIZE (entities) == 0) + { + if (try_unspecified[FONT_WEIGHT_INDEX] + || try_unspecified[FONT_SLANT_INDEX] + || try_unspecified[FONT_WIDTH_INDEX] + || try_unspecified[FONT_SIZE_INDEX]) + { + for (i = FONT_WEIGHT_INDEX; i <= FONT_SIZE_INDEX; i++) + { + try_unspecified[i] = 0; + ASET (scratch_font_spec, i, Qnil); + } + entities = font_list_entities (frame, scratch_font_spec); + } + else if (try_unspecified[FONT_FOUNDRY_INDEX]) + { + try_unspecified[FONT_FOUNDRY_INDEX] = 0; + ASET (scratch_font_spec, FONT_FOUNDRY_INDEX, Qnil); + entities = font_list_entities (frame, scratch_font_spec); + } + else if (try_unspecified[FONT_FAMILY_INDEX]) + { + try_unspecified[FONT_FAMILY_INDEX] = 0; + ASET (scratch_font_spec, FONT_FAMILY_INDEX, Qnil); + entities = font_list_entities (frame, scratch_font_spec); + } + else + return Qnil; + } + + if (ASIZE (entities) > 1) + { + Lisp_Object prefer = scratch_font_prefer; + + for (i = 0; i < FONT_WEIGHT_INDEX; i++) + ASET (prefer, i, Qnil); + if (! NILP (attrs[LFACE_WEIGHT_INDEX])) + ASET (prefer, FONT_WEIGHT_INDEX, + font_prop_validate_style (FONT_WEIGHT_INDEX, + attrs[LFACE_WEIGHT_INDEX])); + if (! NILP (attrs[LFACE_SLANT_INDEX])) + ASET (prefer, FONT_SLANT_INDEX, + font_prop_validate_style (FONT_SLANT_INDEX, + attrs[LFACE_SLANT_INDEX])); + if (! NILP (attrs[LFACE_SWIDTH_INDEX])) + ASET (prefer, FONT_WIDTH_INDEX, + font_prop_validate_style (FONT_WIDTH_INDEX, + attrs[LFACE_SWIDTH_INDEX])); + if (! NILP (attrs[LFACE_HEIGHT_INDEX])) + { + int size; + + val = attrs[LFACE_HEIGHT_INDEX]; + size = POINT_TO_PIXEL (XINT (val), f->resy); + ASET (prefer, FONT_SIZE_INDEX, make_number (size)); + } + font_sort_entites (entities, prefer, frame, spec); + } + + return AREF (entities, 0); +} + +Lisp_Object +font_open_for_lface (f, lface, entity) + FRAME_PTR f; + Lisp_Object *lface; + Lisp_Object entity; +{ + int pt = XINT (lface[LFACE_HEIGHT_INDEX]); + int size = POINT_TO_PIXEL (pt, f->resy); + + return font_open_entity (f, entity, size); +} + +void +font_load_for_face (f, face) + FRAME_PTR f; + struct face *face; +{ + Lisp_Object entity; + + face->font_info_id = -1; + face->font_info = NULL; + face->font = NULL; + face->font_name = NULL; + + entity = font_find_for_lface (f, face->lface, Qnil); + if (! NILP (entity)) + { + Lisp_Object font_object = font_open_for_lface (f, face->lface, entity); + + if (! NILP (font_object)) + { + struct font *font = XSAVE_VALUE (font_object)->pointer; + + face->font = font->font.font; + face->font_info = (struct font_info *) font; + face->font_info_id = 0; + face->font_name = font->font.full_name; + } + } + if (! face->font) + add_to_log ("Unable to load font for a face%s", null_string, Qnil); +} + +void +font_prepare_for_face (f, face) + FRAME_PTR f; + struct face *face; +{ + struct font *font = (struct font *) face->font_info; + + if (font->driver->prepare_face) + font->driver->prepare_face (f, face); +} + +void +font_done_for_face (f, face) + FRAME_PTR f; + struct face *face; +{ + struct font *font = (struct font *) face->font_info; + + if (font->driver->done_face) + font->driver->done_face (f, face); + face->extra = NULL; +} + +Lisp_Object +font_open_by_name (f, name) + FRAME_PTR f; + char *name; +{ + Lisp_Object spec = Ffont_spec (0, NULL); + Lisp_Object entities = Qnil; + Lisp_Object frame; + int pixel_size; + + XSETFRAME (frame, f); + + ASET (spec, FONT_EXTRA_INDEX, + Fcons (Fcons (QCname, make_unibyte_string (name, strlen (name))), + Qnil)); + entities = font_list_entities (frame, spec); + if (ASIZE (entities) == 0) + return Qnil; + pixel_size = XINT (AREF (AREF (entities, 0), FONT_SIZE_INDEX)); + if (pixel_size == 0) + pixel_size = 12; + return font_open_entity (f, AREF (entities, 0), pixel_size); +} + + +/* Register font-driver DRIVER. This function is used in two ways. + + The first is with frame F non-NULL. In this case, DRIVER is + registered to be used for drawing characters on F. All frame + creaters (e.g. Fx_create_frame) must call this function at least + once with an available font-driver. + + The second is with frame F NULL. In this case, DRIVER is globally + registered in the variable `font_driver_list'. All font-driver + implementations must call this function in its syms_of_XXXX + (e.g. syms_of_xfont). */ + +void +register_font_driver (driver, f) + struct font_driver *driver; + FRAME_PTR f; +{ + struct font_driver_list *root = f ? f->font_driver_list : font_driver_list; + struct font_driver_list *prev, *list; + + if (f && ! driver->draw) + error ("Unsable font driver for a frame: %s", + SDATA (SYMBOL_NAME (driver->type))); + + for (prev = NULL, list = root; list; prev = list, list = list->next) + if (list->driver->type == driver->type) + error ("Duplicated font driver: %s", SDATA (SYMBOL_NAME (driver->type))); + + list = malloc (sizeof (struct font_driver_list)); + list->driver = driver; + list->next = NULL; + if (prev) + prev->next = list; + else if (f) + f->font_driver_list = list; + else + font_driver_list = list; + num_font_drivers++; +} + +/* Free font-driver list on frame F. It doesn't free font-drivers + themselves. */ + +void +free_font_driver_list (f) + FRAME_PTR f; +{ + while (f->font_driver_list) + { + struct font_driver_list *next = f->font_driver_list->next; + + free (f->font_driver_list); + f->font_driver_list = next; + } +} + + +/* Lisp API */ + +DEFUN ("fontp", Ffontp, Sfontp, 1, 1, 0, + doc: /* Return t if object is a font-spec or font-entity. */) + (object) + Lisp_Object object; +{ + return (FONTP (object) ? Qt : Qnil); +} + +DEFUN ("font-spec", Ffont_spec, Sfont_spec, 0, MANY, 0, + doc: /* Return a newly created font-spec with specified arguments as properties. +usage: (font-spec &rest properties) */) + (nargs, args) + int nargs; + Lisp_Object *args; +{ + Lisp_Object spec = Fmake_vector (make_number (FONT_SPEC_MAX), Qnil); + Lisp_Object extra = Qnil; + int i; + + for (i = 0; i < nargs; i += 2) + { + enum font_property_index prop; + Lisp_Object key = args[i], val = args[i + 1]; + + prop = check_font_prop_name (key); + if (prop < FONT_EXTRA_INDEX) + ASET (spec, prop, (font_property_table[prop].validater) (prop, val)); + else + extra = Fcons (Fcons (key, val), extra); + } + ASET (spec, FONT_EXTRA_INDEX, extra); + return spec; +} + + +DEFUN ("font-get", Ffont_get, Sfont_get, 2, 2, 0, + doc: /* Return the value of FONT's PROP property. +FONT may be a font-spec or font-entity. +If FONT is font-entity and PROP is :extra, always nil is returned. */) + (font, prop) + Lisp_Object font, prop; +{ + enum font_property_index idx; + + CHECK_FONT (font); + idx = check_font_prop_name (prop); + if (idx < FONT_EXTRA_INDEX) + return AREF (font, idx); + if (FONT_ENTITY_P (font)) + return Qnil; + return Fcdr (Fassoc (AREF (font, FONT_EXTRA_INDEX), prop)); +} + + +DEFUN ("font-put", Ffont_put, Sfont_put, 3, 3, 0, + doc: /* Set one property of FONT-SPEC: give property PROP value VALUE. */) + (font_spec, prop, val) + Lisp_Object font_spec, prop, val; +{ + enum font_property_index idx; + Lisp_Object extra, slot; + + CHECK_FONT_SPEC (font_spec); + idx = check_font_prop_name (prop); + if (idx < FONT_EXTRA_INDEX) + return ASET (font_spec, idx, val); + extra = AREF (font_spec, FONT_EXTRA_INDEX); + slot = Fassoc (extra, prop); + if (NILP (slot)) + extra = Fcons (Fcons (prop, val), extra); + else + Fsetcdr (slot, val); + return val; +} + +DEFUN ("list-fonts", Flist_fonts, Slist_fonts, 1, 4, 0, + doc: /* List available fonts matching FONT-SPEC on the current frame. +Optional 2nd argument FRAME specifies the target frame. +Optional 3rd argument NUM, if non-nil, limits the number of returned fonts. +Optional 4th argument PREFER, if non-nil, is a font-spec to sort fonts +by closeness to PREFER. */) + (font_spec, frame, num, prefer) + Lisp_Object font_spec, frame, num, prefer; +{ + Lisp_Object vec, list, tail; + int n = 0, i, len; + + if (NILP (frame)) + frame = selected_frame; + CHECK_LIVE_FRAME (frame); + CHECK_VALIDATE_FONT_SPEC (font_spec); + if (! NILP (num)) + { + CHECK_NUMBER (num); + n = XINT (num); + if (n <= 0) + return Qnil; + } + if (! NILP (prefer)) + CHECK_FONT (prefer); + + vec = font_list_entities (frame, font_spec); + len = ASIZE (vec); + if (len == 0) + return Qnil; + if (len == 1) + return Fcons (AREF (vec, 0), Qnil); + + if (! NILP (prefer)) + vec = font_sort_entites (vec, prefer, frame, font_spec); + + list = tail = Fcons (AREF (vec, 0), Qnil); + if (n == 0 || n > len) + n = len; + for (i = 1; i < n; i++) + { + Lisp_Object val = Fcons (AREF (vec, i), Qnil); + + XSETCDR (tail, val); + tail = val; + } + return list; +} + +DEFUN ("list-families", Flist_families, Slist_families, 0, 1, 0, + doc: /* List available font families on the current frame. +Optional 2nd argument FRAME specifies the target frame. */) + (frame) + Lisp_Object frame; +{ + FRAME_PTR f; + struct font_driver_list *driver_list; + Lisp_Object list; + + if (NILP (frame)) + frame = selected_frame; + CHECK_LIVE_FRAME (frame); + f = XFRAME (frame); + list = Qnil; + for (driver_list = f->font_driver_list; driver_list; + driver_list = driver_list->next) + if (driver_list->driver->list_family) + { + Lisp_Object val = driver_list->driver->list_family (frame); + + if (NILP (list)) + list = val; + else + { + Lisp_Object tail = list; + + for (; CONSP (val); val = XCDR (val)) + if (NILP (Fmemq (XCAR (val), tail))) + list = Fcons (XCAR (val), list); + } + } + return list; +} + +DEFUN ("find-font", Ffind_font, Sfind_font, 1, 2, 0, + doc: /* Return a font-entity matching with FONT-SPEC on the current frame. +Optional 2nd argument FRAME, if non-nil, specifies the target frame. */) + (font_spec, frame) + Lisp_Object font_spec, frame; +{ + Lisp_Object val = Flist_fonts (font_spec, frame, make_number (1), Qnil); + + if (CONSP (val)) + val = XCAR (val); + return val; +} + +DEFUN ("font-xlfd-name", Ffont_xlfd_name, Sfont_xlfd_name, 1, 1, 0, + doc: /* Return XLFD name of FONT. +FONT is a font-spec, font-entity, or font-object. +If the name is too long for XLFD (maximum 255 chars), return nil. */) + (font) + Lisp_Object font; +{ + char name[256]; + int pixel_size = 0; + + if (FONT_SPEC_P (font)) + CHECK_VALIDATE_FONT_SPEC (font); + else if (FONT_ENTITY_P (font)) + CHECK_FONT (font); + else + { + struct font *fontp; + + CHECK_FONT_GET_OBJECT (font, fontp); + font = fontp->entity; + pixel_size = fontp->pixel_size; + } + + if (font_unparse_xlfd (font, pixel_size, name, 256) < 0) + return Qnil; + return build_string (name); +} + +DEFUN ("clear-font-cache", Fclear_font_cache, Sclear_font_cache, 0, 0, 0, + doc: /* Clear font cache. */) + () +{ + Lisp_Object list, frame; + + FOR_EACH_FRAME (list, frame) + { + FRAME_PTR f = XFRAME (frame); + struct font_driver_list *driver_list = f->font_driver_list; + + for (; driver_list; driver_list = driver_list->next) + { + Lisp_Object cache = driver_list->driver->get_cache (frame); + Lisp_Object tail, elt; + + for (tail = XCDR (cache); CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + if (CONSP (elt) && FONT_SPEC_P (XCAR (elt))) + { + Lisp_Object vec = XCDR (elt); + int i; + + for (i = 0; i < ASIZE (vec); i++) + { + Lisp_Object entity = AREF (vec, i); + Lisp_Object objlist = AREF (entity, FONT_OBJLIST_INDEX); + + for (; CONSP (objlist); objlist = XCDR (objlist)) + { + Lisp_Object val = XCAR (objlist); + struct Lisp_Save_Value *p = XSAVE_VALUE (val); + struct font *font = p->pointer; + + xassert (font + && driver_list->driver == font->driver); + driver_list->driver->close (f, font); + p->pointer = NULL; + p->integer = 0; + } + if (driver_list->driver->free_entity) + driver_list->driver->free_entity (entity); + } + } + } + XSETCDR (cache, Qnil); + } + } + + return Qnil; +} + +DEFUN ("internal-set-font-style-table", Finternal_set_font_style_table, + Sinternal_set_font_style_table, 2, 2, 0, + doc: /* Set font style table for PROP to TABLE. +PROP must be `:weight', `:slant', or `:width'. +TABLE must be an alist of symbols vs the corresponding numeric values +sorted by numeric values. */) + (prop, table) + Lisp_Object prop, table; +{ + int table_index; + int numeric; + Lisp_Object tail, val; + + CHECK_SYMBOL (prop); + table_index = (EQ (prop, QCweight) ? 0 + : EQ (prop, QCslant) ? 1 + : EQ (prop, QCwidth) ? 2 + : 3); + if (table_index >= ASIZE (font_style_table)) + error ("Invalid font style property: %s", SDATA (SYMBOL_NAME (prop))); + table = Fcopy_sequence (table); + numeric = -1; + for (tail = table; ! NILP (tail); tail = Fcdr (tail)) + { + prop = Fcar (Fcar (tail)); + val = Fcdr (Fcar (tail)); + CHECK_SYMBOL (prop); + CHECK_NATNUM (val); + if (numeric > XINT (val)) + error ("Numeric values not sorted for %s", SDATA (SYMBOL_NAME (prop))); + numeric = XINT (val); + XSETCAR (tail, Fcons (prop, val)); + } + ASET (font_style_table, table_index, table); + return Qnil; +} + +DEFUN ("font-make-gstring", Ffont_make_gstring, Sfont_make_gstring, 2, 2, 0, + doc: /* Return a newly created glyph-string for FONT-OBJECT with NUM glyphs. +FONT-OBJECT may be nil if it is not yet known. */) + (font_object, num) + Lisp_Object font_object, num; +{ + Lisp_Object gstring, g; + int len; + int i; + + if (! NILP (font_object)) + CHECK_FONT_OBJECT (font_object); + CHECK_NATNUM (num); + + len = XINT (num) + 1; + gstring = Fmake_vector (make_number (len), Qnil); + g = Fmake_vector (make_number (6), Qnil); + ASET (g, 0, font_object); + ASET (gstring, 0, g); + for (i = 1; i < len; i++) + ASET (gstring, i, Fmake_vector (make_number (8), make_number (0))); + return gstring; +} + +DEFUN ("font-fill-gstring", Ffont_fill_gstring, Sfont_fill_gstring, 4, 5, 0, + doc: /* Fillin glyph-string GSTRING by characters for FONT-OBJECT. +START and END specifies the region to extract characters. +If optional 3rd argument OBJECT is non-nil, it is a buffer or a string from +where to extract characters. +FONT-OBJECT may be nil if GSTRING already already contains one. */) + (gstring, font_object, start, end, object) + Lisp_Object gstring, font_object, start, end, object; +{ + int len, i, c; + unsigned code; + struct font *font; + + CHECK_VECTOR (gstring); + if (NILP (font_object)) + font_object = Faref (Faref (gstring, make_number (0)), make_number (0)); + CHECK_FONT_GET_OBJECT (font_object, font); + + if (STRINGP (object)) + { + const unsigned char *p; + + CHECK_NATNUM (start); + CHECK_NATNUM (end); + if (XINT (start) > XINT (end) + || XINT (end) > ASIZE (object) + || XINT (end) - XINT (start) >= XINT (Flength (gstring))) + args_out_of_range (start, end); + + len = XINT (end) - XINT (start); + p = SDATA (object) + string_char_to_byte (object, XINT (start)); + for (i = 0; i < len; i++) + { + Lisp_Object g = LGSTRING_GLYPH (gstring, i); + + c = STRING_CHAR_ADVANCE (p); + code = font->driver->encode_char (font, c); + if (code > MOST_POSITIVE_FIXNUM) + error ("Glyph code 0x%X is too large", code); + ASET (g, 0, make_number (i)); + ASET (g, 1, make_number (i + 1)); + LGLYPH_SET_CHAR (g, make_number (c)); + LGLYPH_SET_CODE (g, make_number (code)); + } + } + else + { + int pos, pos_byte; + + if (! NILP (object)) + Fset_buffer (object); + validate_region (&start, &end); + if (XINT (end) - XINT (start) > len) + args_out_of_range (start, end); + len = XINT (end) - XINT (start); + pos = XINT (start); + pos_byte = CHAR_TO_BYTE (pos); + for (i = 0; i < len; i++) + { + Lisp_Object g = LGSTRING_GLYPH (gstring, i); + + FETCH_CHAR_ADVANCE (c, pos, pos_byte); + code = font->driver->encode_char (font, c); + if (code > MOST_POSITIVE_FIXNUM) + error ("Glyph code 0x%X is too large", code); + ASET (g, 0, make_number (i)); + ASET (g, 1, make_number (i + 1)); + LGLYPH_SET_CHAR (g, make_number (c)); + LGLYPH_SET_CODE (g, make_number (code)); + } + } + return Qnil; +} + + +#ifdef FONT_DEBUG + +DEFUN ("open-font", Fopen_font, Sopen_font, 1, 3, 0, + doc: /* Open FONT-ENTITY. */) + (font_entity, size, frame) + Lisp_Object font_entity; + Lisp_Object size; + Lisp_Object frame; +{ + int isize; + + CHECK_FONT_ENTITY (font_entity); + if (NILP (size)) + size = AREF (font_entity, FONT_SIZE_INDEX); + CHECK_NUMBER (size); + if (NILP (frame)) + frame = selected_frame; + CHECK_LIVE_FRAME (frame); + + isize = XINT (size); + if (isize < 0) + isize = POINT_TO_PIXEL (- isize, XFRAME (frame)->resy); + + return font_open_entity (XFRAME (frame), font_entity, isize); +} + +DEFUN ("close-font", Fclose_font, Sclose_font, 1, 2, 0, + doc: /* Close FONT-OBJECT. */) + (font_object, frame) + Lisp_Object font_object, frame; +{ + CHECK_FONT_OBJECT (font_object); + if (NILP (frame)) + frame = selected_frame; + CHECK_LIVE_FRAME (frame); + font_close_object (XFRAME (frame), font_object); + return Qnil; +} + +DEFUN ("query-font", Fquery_font, Squery_font, 1, 1, 0, + doc: /* Return information about FONT-OBJECT. */) + (font_object) + Lisp_Object font_object; +{ + struct font *font; + Lisp_Object val; + + CHECK_FONT_GET_OBJECT (font_object, font); + + val = Fmake_vector (make_number (9), Qnil); + ASET (val, 0, Ffont_xlfd_name (font_object)); + if (font->file_name) + ASET (val, 1, make_unibyte_string (font->file_name, + strlen (font->file_name))); + ASET (val, 2, make_number (font->pixel_size)); + ASET (val, 3, make_number (font->font.size)); + ASET (val, 4, make_number (font->ascent)); + ASET (val, 5, make_number (font->descent)); + ASET (val, 6, make_number (font->font.space_width)); + ASET (val, 7, make_number (font->font.average_width)); + if (font->driver->otf_capability) + ASET (val, 8, font->driver->otf_capability (font)); + return val; +} + +DEFUN ("get-font-glyphs", Fget_font_glyphs, Sget_font_glyphs, 2, 2, 0, + doc: /* Return a vector of glyphs of FONT-OBJECT for drawing STRING. +Each element is a vector [GLYPH-CODE LBEARING RBEARING WIDTH ASCENT DESCENT]. */) + (font_object, string) + Lisp_Object font_object, string; +{ + struct font *font; + int i, len; + Lisp_Object vec; + + CHECK_FONT_GET_OBJECT (font_object, font); + CHECK_STRING (string); + len = SCHARS (string); + vec = Fmake_vector (make_number (len), Qnil); + for (i = 0; i < len; i++) + { + Lisp_Object ch = Faref (string, make_number (i)); + Lisp_Object val; + int c = XINT (ch); + unsigned code; + struct font_metrics metrics; + + code = font->driver->encode_char (font, c); + if (code == FONT_INVALID_CODE) + continue; + val = Fmake_vector (make_number (6), Qnil); + if (code <= MOST_POSITIVE_FIXNUM) + ASET (val, 0, make_number (code)); + else + ASET (val, 0, Fcons (make_number (code >> 16), + make_number (code & 0xFFFF))); + font->driver->text_extents (font, &code, 1, &metrics); + ASET (val, 1, make_number (metrics.lbearing)); + ASET (val, 2, make_number (metrics.rbearing)); + ASET (val, 3, make_number (metrics.width)); + ASET (val, 4, make_number (metrics.ascent)); + ASET (val, 5, make_number (metrics.descent)); + ASET (vec, i, val); + } + return vec; +} + +#if 0 +DEFUN ("draw-string", Fdraw_string, Sdraw_string, 2, 2, 0, + doc: /* Draw STRING by FONT-OBJECT on the top left corner of the current frame. +The value is a number of glyphs drawn. +Type C-l to recover what previously shown. */) + (font_object, string) + Lisp_Object font_object, string; +{ + Lisp_Object frame = selected_frame; + FRAME_PTR f = XFRAME (frame); + struct font *font; + struct face *face; + int i, len, width; + unsigned *code; + + CHECK_FONT_GET_OBJECT (font_object, font); + CHECK_STRING (string); + len = SCHARS (string); + code = alloca (sizeof (unsigned) * len); + for (i = 0; i < len; i++) + { + Lisp_Object ch = Faref (string, make_number (i)); + Lisp_Object val; + int c = XINT (ch); + + code[i] = font->driver->encode_char (font, c); + if (code[i] == FONT_INVALID_CODE) + break; + } + face = FACE_FROM_ID (f, DEFAULT_FACE_ID); + face->fontp = font; + if (font->driver->prepare_face) + font->driver->prepare_face (f, face); + width = font->driver->text_extents (font, code, i, NULL); + len = font->driver->draw_text (f, face, 0, font->ascent, code, i, width); + if (font->driver->done_face) + font->driver->done_face (f, face); + face->fontp = NULL; + return make_number (len); +} +#endif + +#endif /* FONT_DEBUG */ + + +extern void syms_of_ftfont P_ (()); +extern void syms_of_xfont P_ (()); +extern void syms_of_xftfont P_ (()); +extern void syms_of_ftxfont P_ (()); +extern void syms_of_bdffont P_ (()); +extern void syms_of_w32font P_ (()); +extern void syms_of_atmfont P_ (()); + +void +syms_of_font () +{ + sort_shift_bits[FONT_SLANT_INDEX] = 0; + sort_shift_bits[FONT_WEIGHT_INDEX] = 7; + sort_shift_bits[FONT_SIZE_INDEX] = 14; + sort_shift_bits[FONT_WIDTH_INDEX] = 21; + sort_shift_bits[FONT_ADSTYLE_INDEX] = 28; + sort_shift_bits[FONT_FOUNDRY_INDEX] = 29; + sort_shift_bits[FONT_FAMILY_INDEX] = 30; + sort_shift_bits[FONT_REGISTRY_INDEX] = 31; + /* Note that sort_shift_bits[FONT_SLANT_TYPE] is never used. */ + + staticpro (&font_style_table); + font_style_table = Fmake_vector (make_number (3), Qnil); + + staticpro (&font_family_alist); + font_family_alist = Qnil; + + DEFSYM (Qfontp, "fontp"); + + DEFSYM (QCotf, ":otf"); + DEFSYM (QClanguage, ":language"); + DEFSYM (QCscript, ":script"); + + DEFSYM (QCfoundry, ":foundry"); + DEFSYM (QCadstyle, ":adstyle"); + DEFSYM (QCregistry, ":registry"); + DEFSYM (QCextra, ":extra"); + + staticpro (&null_string); + null_string = build_string (""); + staticpro (&null_vector); + null_vector = Fmake_vector (make_number (0), Qnil); + + staticpro (&scratch_font_spec); + scratch_font_spec = Ffont_spec (0, NULL); + staticpro (&scratch_font_prefer); + scratch_font_prefer = Ffont_spec (0, NULL); + + defsubr (&Sfontp); + defsubr (&Sfont_spec); + defsubr (&Sfont_get); + defsubr (&Sfont_put); + defsubr (&Slist_fonts); + defsubr (&Slist_families); + defsubr (&Sfind_font); + defsubr (&Sfont_xlfd_name); + defsubr (&Sclear_font_cache); + defsubr (&Sinternal_set_font_style_table); + defsubr (&Sfont_make_gstring); + defsubr (&Sfont_fill_gstring); + +#ifdef FONT_DEBUG + defsubr (&Sopen_font); + defsubr (&Sclose_font); + defsubr (&Squery_font); + defsubr (&Sget_font_glyphs); +#if 0 + defsubr (&Sdraw_string); +#endif +#endif /* FONT_DEBUG */ + +#ifdef HAVE_FREETYPE + syms_of_ftfont (); +#ifdef HAVE_X_WINDOWS + syms_of_xfont (); + syms_of_ftxfont (); +#ifdef HAVE_XFT + syms_of_xftfont (); +#endif /* HAVE_XFT */ +#endif /* HAVE_X_WINDOWS */ +#else /* not HAVE_FREETYPE */ +#ifdef HAVE_X_WINDOWS + syms_of_xfont (); +#endif /* HAVE_X_WINDOWS */ +#endif /* not HAVE_FREETYPE */ +#ifdef HAVE_BDFFONT + syms_of_bdffont (); +#endif /* HAVE_BDFFONT */ +#ifdef WINDOWSNT + syms_of_w32font (); +#endif /* WINDOWSNT */ +#ifdef MAC_OS + syms_of_atmfont (); +#endif /* MAC_OS */ +} diff --git a/src/font.h b/src/font.h new file mode 100644 index 0000000000..3af90f5ddc --- /dev/null +++ b/src/font.h @@ -0,0 +1,479 @@ +/* font.h -- Interface definition for font handling. + Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H13PRO009 + +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 2, 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; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#ifndef EMACS_FONT_H +#define EMACS_FONT_H + +#include "ccl.h" + +/* We have three types of Lisp objects related to font. + + FONT-SPEC + + Vector (length FONT_SPEC_MAX) of font properties. Some + properties can be left unspecified (i.e. nil). Emacs asks + font-drivers to find a font by FONT-SPEC. A fontset entry + specifies requisite properties whereas a face specifies just + preferable properties. This object is fully modifiable by + Lisp. + + FONT-ENTITY + + Vector (length FONT_ENTITY_MAX) of fully specified font + properties that a font-driver returns upon a request of + FONT-SPEC. + + Note: Only the method `list' of a font-driver can create this + object, and should never be modified by Lisp. In that sense, + it may be cleaner to implement it as a Lisp object of a new + type (e.g. struct Lisp_Font). + + FONT-OBJECT + + Lisp object of type Lisp_Misc_Save_Value encapsulating a + pointer to "struct font". This corresponds to an opened font. + + Note: The note for FONT-ENTITY also applies to this. +*/ + + +struct font_driver; +struct font; + +/* An enumerator for each font property. This is used as an index to + the vector of FONT-SPEC and FONT-ENTITY. + + Note: The order is important and should not be changed. */ + +enum font_property_index + { + /* FONT-TYPE is a symbol indicating a font backend; currently `x', + `xft', `ftx', `freetype' are available. For windows, we need + `bdf' and `windows'. For Mac OS X, we need `atm'. */ + FONT_TYPE_INDEX, + + /* FONT-FOUNDRY is a foundry name (symbol). */ + FONT_FOUNDRY_INDEX, + + /* FONT-FAMILY is a family name (symbol). */ + FONT_FAMILY_INDEX, + + /* FONT-ADSTYLE is an additional style name (symbol). */ + FONT_ADSTYLE_INDEX, + + /* FONT-REGISTRY is a combination of a charset-registry and + charset0encoding name (symbol). */ + FONT_REGISTRY_INDEX, + + /* FONT-WEIGHT is a numeric value of weight (e.g. medium, bold) of + the font. The value is what defined by FC_WEIGHT_* in + fontconfig. */ + FONT_WEIGHT_INDEX, + + /* FONT-SLANT is a numeric value of slant (e.g. r, i, o) of the + font. The value is what defined by FC_SLANT_* in + fontconfig plus 100. */ + FONT_SLANT_INDEX, + + /* FONT-WIDTH is a numeric value of setwidth (e.g. normal, + condensed) of the font. The value is what defined by + FC_WIDTH_* in fontconfig. */ + FONT_WIDTH_INDEX, + + /* FONT-SIZE is a size of the font. If integer, it is a pixel + size. For a font-spec, the value can be float specifying a + point size. For a font-entity, the value can be zero meaning + that the font is scalable. */ + FONT_SIZE_INDEX, + + /* In a font-spec, the value is an alist of extra information of a + font such as name, OpenType features, and language coverage. + In a font-entity, the value is an extra infomation for + identifying a font (font-driver dependent). */ + FONT_EXTRA_INDEX, /* alist alist */ + + /* This value is the length of font-spec vector. */ + FONT_SPEC_MAX, + + /* The followings are used only for a font-entity. */ + + /* Frame on which the font is found. The value is nil if the font + can be opend on any frame. */ + FONT_FRAME_INDEX = FONT_SPEC_MAX, + + /* List of font-objects opened from the font-entity. */ + FONT_OBJLIST_INDEX, + + /* This value is the length of font-entity vector. */ + FONT_ENTITY_MAX + }; + +extern Lisp_Object QCotf, QClanguage, QCscript; + +extern Lisp_Object null_string; +extern Lisp_Object null_vector; + +/* Structure for an opened font. We can safely cast this structure to + "struft font_info". */ + +struct font +{ + struct font_info font; + + /* From which font-entity the font is opened. */ + Lisp_Object entity; + + /* By which pixel size the font is opened. */ + int pixel_size; + + /* Font-driver for the font. */ + struct font_driver *driver; + + /* File name of the font, or NULL if the font is not associated with + a file. */ + char *file_name; + + /* Charset to encode a character code into a glyph code of the + font. */ + int encoding_charset; + + /* Charset to check if a character code is supported by the font. + -1 means that the contents of the font must be looked up to + determine it. + */ + int repertory_charet; + + /* Minimum glyph width (in pixels). */ + int min_width; + + /* Ascent and descent of the font (in pixels). */ + int ascent, descent; + + /* There will be more to this structure, but they are private to a + font-driver. */ +}; + +struct font_metrics +{ + short lbearing, rbearing, width, ascent, descent; +}; + +struct font_bitmap +{ + int rows; + int width; + int pitch; + unsigned char *buffer; + int left; + int top; + int advance; + void *extra; +}; + +/* Predicates to check various font-related objects. */ + +#define FONTP(x) \ + (VECTORP (x) && (ASIZE (x) == FONT_SPEC_MAX || ASIZE (x) == FONT_ENTITY_MAX)) +#define FONT_SPEC_P(x) \ + (VECTORP (x) && ASIZE (x) == FONT_SPEC_MAX) +#define FONT_ENTITY_P(x) \ + (VECTORP (x) && ASIZE (x) == FONT_ENTITY_MAX) +#define FONT_OBJECT_P(x) \ + (XTYPE (x) == Lisp_Misc && XMISCTYPE (x) == Lisp_Misc_Save_Value) + + +/* Check macros for various font-related objects. */ + +#define CHECK_FONT(x) \ + do { if (! FONTP (x)) x = wrong_type_argument (Qfont, x); } while (0) +#define CHECK_FONT_SPEC(x) \ + do { if (! FONT_SPEC_P (x)) x = wrong_type_argument (Qfont, x); } while (0) +#define CHECK_FONT_ENTITY(x) \ + do { if (! FONT_ENTITY_P (x)) x = wrong_type_argument (Qfont, x); } while (0) +#define CHECK_FONT_OBJECT(x) \ + do { if (! FONT_OBJECT_P (x)) x = wrong_type_argument (Qfont, x); } while (0) + +#define CHECK_FONT_GET_OBJECT(x, font) \ + do { \ + if (! FONT_OBJECT_P (x)) x = wrong_type_argument (Qfont, x); \ + if (! XSAVE_VALUE (x)->pointer) error ("Font already closed"); \ + font = XSAVE_VALUE (x)->pointer; \ + } while (0) + +struct face; +struct composition; + +/* Macros for lispy glyph-string. */ +#define LGSTRING_FONT(lgs) AREF (AREF ((lgs), 0), 0) +#define LGSTRING_LBEARING(lgs) AREF (AREF ((lgs), 0), 1) +#define LGSTRING_RBEARING(lgs) AREF (AREF ((lgs), 0), 2) +#define LGSTRING_WIDTH(lgs) AREF (AREF ((lgs), 0), 3) +#define LGSTRING_ASCENT(lgs) AREF (AREF ((lgs), 0), 4) +#define LGSTRING_DESCENT(lgs) AREF (AREF ((lgs), 0), 5) +#define LGSTRING_SET_FONT(lgs, val) ASET (AREF ((lgs), 0), 0, (val)) +#define LGSTRING_SET_LBEARING(lgs, val) ASET (AREF ((lgs), 0), 1, (val)) +#define LGSTRING_SET_RBEARING(lgs, val) ASET (AREF ((lgs), 0), 2, (val)) +#define LGSTRING_SET_WIDTH(lgs, val) ASET (AREF ((lgs), 0), 3, (val)) +#define LGSTRING_SET_ASCENT(lgs, val) ASET (AREF ((lgs), 0), 4, (val)) +#define LGSTRING_SET_DESCENT(lgs, val) ASET (AREF ((lgs), 0), 5, (val)) + +#define LGSTRING_LENGTH(lgs) (ASIZE ((lgs)) - 1) +#define LGSTRING_GLYPH(lgs, idx) AREF ((lgs), (idx) + 1) + +#define LGLYPH_CHAR(g) AREF ((g), 2) +#define LGLYPH_CODE(g) AREF ((g), 3) +#define LGLYPH_XOFF(g) AREF ((g), 4) +#define LGLYPH_YOFF(g) AREF ((g), 5) +#define LGLYPH_WIDTH(g) AREF ((g), 6) +#define LGLYPH_WADJUST(g) AREF ((g), 7) +#define LGLYPH_SET_CHAR(g, val) ASET ((g), 2, (val)) +#define LGLYPH_SET_CODE(g, val) ASET ((g), 3, (val)) +#define LGLYPH_SET_XOFF(g, val) ASET ((g), 4, (val)) +#define LGLYPH_SET_YOFF(g, val) ASET ((g), 5, (val)) +#define LGLYPH_SET_WIDTH(g, val) ASET ((g), 6, (val)) +#define LGLYPH_SET_WADJUST(g, val) ASET ((g), 7, (val)) + +#define FONT_INVALID_CODE 0xFFFFFFFF + +struct font_driver +{ + /* Symbol indicating the type of the font-driver. */ + Lisp_Object type; + + /* Return a cache of font-entities on FRAME. The cache must be a + cons whose cdr part is the actual cache area. */ + Lisp_Object (*get_cache) P_ ((Lisp_Object frame)); + + /* Parse font name NAME, store the font properties in SPEC, and + return 0. If the font-driver can't parse NAME, return -1. */ + int (*parse_name) P_ ((FRAME_PTR f, char *name, Lisp_Object spec)); + + /* List fonts matching with FONT_SPEC on FRAME. The value is a + vector of font-entities. This is the sole API that allocates + font-entities. */ + Lisp_Object (*list) P_ ((Lisp_Object frame, Lisp_Object font_spec)); + + /* List available families. The value is a list of family names + (symbols). The method can be NULL if the driver doesn't support + this facility. */ + Lisp_Object (*list_family) P_ ((Lisp_Object frame)); + + /* Free FONT_EXTRA_INDEX field of FONT_ENTITY. This method can be + NULL if FONT_EXTRA_INDEX of FONT_ENTITY is a normal Lisp object + (i.e. not Lisp_Save_Value). */ + void (*free_entity) P_ ((Lisp_Object font_entity)); + + /* Open a font specified by FONT_ENTITY on frame F. If the font is + scalable, open it with PIXEL_SIZE. */ + struct font *(*open) P_ ((FRAME_PTR f, Lisp_Object font_entity, + int pixel_size)); + + /* Close FONT on frame F. */ + void (*close) P_ ((FRAME_PTR f, struct font *font)); + + /* Prepare FACE for displaying characters by FONT on frame F. If + successful, return 0. Otherwise, return -1. This method can be + NULL if there's nothing to do. */ + int (*prepare_face) P_ ((FRAME_PTR f, struct face *face)); + + /* Done FACE for displaying characters by FACE->font on frame F. + This method can be NULL if there's nothing to do. */ + void (*done_face) P_ ((FRAME_PTR f, struct face *face)); + + /* If FONT_ENTITY has a glyph for character C, return 1. If not, + return 0. If a font must be opened to check it, return -1. This + method can be NULL if the driver always requires a font to be + opened for this check. In that case, we must open a font and use + `encode_char' method. */ + int (*has_char) P_ ((Lisp_Object entity, int c)); + + /* Return a glyph code of FONT for characer C. If FONT doesn't have + such a glyph, return FONT_INVALID_CODE. */ + unsigned (*encode_char) P_ ((struct font *font, int c)); + + /* Perform the size computation of glyphs of FONT and fillin members + of METRICS. The glyphs are specified by their glyph codes in + CODE (length NGLYPHS). */ + int (*text_extents) P_ ((struct font *font, + unsigned *code, int nglyphs, + struct font_metrics *metrics)); + + /* Draw glyphs between FROM and TO of S->char2b at (X Y) pixel + position of frame F with S->FACE and S->GC. If WITH_BACKGROUND + is nonzero, fill the background in advance. It is assured that + WITH_BACKGROUND is zero when (FROM > 0 || TO < S->nchars). */ + int (*draw) P_ ((struct glyph_string *s, int from, int to, + int x, int y, int with_background)); + + /* Store bitmap data for glyph-code CODE of FONT in BITMAP. This + method can be NULL if the driver doesn't support this facility. + It is intended that this method is callled from the other + font-driver for actual drawing. */ + int (*get_bitmap) P_ ((struct font *font, unsigned code, + struct font_bitmap *bitmap, + int bits_per_pixel)); + + /* Free bitmap data in BITMAP. This method can be NULL if no data + have to be freed. */ + void (*free_bitmap) P_ ((struct font *font, struct font_bitmap *bitmap)); + + /* Return an outline data for glyph-code CODE of FONT. The format + of the outline data depends on the font-driver. This method can + be NULL if the driver doesn't support this facility. */ + void *(*get_outline) P_ ((struct font *font, unsigned code)); + + /* Free OUTLINE (that is obtained by the above method). */ + void (*free_outline) P_ ((struct font *font, void *outline)); + + /* Get coordinates of the INDEXth anchor point of the glyph whose + code is CODE. Store the coordinates in *X and *Y. Return 0 if + the operations was successfull. Otherwise return -1. This + method can be NULL if the driver doesn't support this + facility. */ + int (*anchor_point) P_ ((struct font *font, unsigned code, int index, + int *x, int *y)); + + /* Return a list describing which scripts/languages FONT + supports by which GSUB/GPOS features of OpenType tables. */ + Lisp_Object (*otf_capability) P_ ((struct font *font)); + + /* Drive FONT's OTF GSUB features according to GSUB_SPEC. + + GSUB_SPEC is in this format (all elements are symbols): + (SCRIPT LANGSYS GSUB-FEATURE ...) + If one of GSUB-FEATURE is nil, apply all gsub features except for + already applied and listed later. For instance, if the font has + GSUB features nukt, haln, rphf, blwf, and half, + (deva nil nukt haln nil rphf) + applies nukt and haln in this order, then applies blwf and half + in the order apearing in the font. The features are of the + default langsys of `deva' script. + + This method applies the specified features to the codes in the + elements of GSTRING-IN (between FROMth and TOth). The output + codes are stored in GSTRING-OUT at the IDXth element and the + following elements. + + Return the number of output codes. If none of the features are + applicable to the input data, return 0. If GSTRING-OUT is too + short, return -1. */ + int (*otf_gsub) P_ ((struct font *font, Lisp_Object gsub_spec, + Lisp_Object gstring_in, int from, int to, + Lisp_Object gstring_out, int idx)); + + /* Drive FONT's OTF GPOS features according to GPOS_SPEC. + + GPOS_SPEC is in this format (all elements are symbols): + (SCRIPT LANGSYS GPOS-FEATURE ...) + The meaning is the same as GSUB_SPEC above. + + This method applies the specified features to the codes in the + elements of GSTRING (between FROMth and TOth). The resulting + positioning information (x-offset and y-offset) is stored in the + slots of the elements. + + Return 1 if at least one glyph has nonzero x-offset or y-offset. + Otherwise return 0. */ + int (*otf_gpos) P_ ((struct font *font, Lisp_Object gpos_spec, + Lisp_Object gstring, int from, int to)); +}; + + +struct font_driver_list +{ + struct font_driver *driver; + struct font_driver_list *next; +}; + +extern int enable_font_backend; + +EXFUN (Ffont_spec, MANY); + +extern Lisp_Object font_symbolic_weight P_ ((Lisp_Object font)); +extern Lisp_Object font_symbolic_slant P_ ((Lisp_Object font)); +extern Lisp_Object font_symbolic_width P_ ((Lisp_Object font)); + +extern Lisp_Object font_find_object P_ ((struct font *font)); +extern char *font_get_name P_ ((Lisp_Object)); +extern Lisp_Object font_get_frame P_ ((Lisp_Object font)); +extern int font_has_char P_ ((FRAME_PTR, Lisp_Object, int)); +extern unsigned font_encode_char P_ ((Lisp_Object, int)); + +extern int font_set_lface_from_name P_ ((FRAME_PTR f, + Lisp_Object lface, + Lisp_Object fontname, + int force_p, int may_fail_p)); +extern Lisp_Object font_find_for_lface P_ ((FRAME_PTR f, Lisp_Object *lface, + Lisp_Object spec)); +extern Lisp_Object font_open_for_lface P_ ((FRAME_PTR f, Lisp_Object *lface, + Lisp_Object entity)); +extern void font_load_for_face P_ ((FRAME_PTR f, struct face *face)); +extern void font_prepare_for_face P_ ((FRAME_PTR f, struct face *face)); +extern Lisp_Object font_open_by_name P_ ((FRAME_PTR f, char *name)); + +extern Lisp_Object intern_downcase P_ ((char *str, int len)); +extern void font_update_sort_order P_ ((int *order)); + +extern void font_parse_old_font_spec P_ ((Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object)); + + +extern int font_parse_xlfd P_ ((char *name, Lisp_Object font, int merge)); +extern int font_unparse_xlfd P_ ((Lisp_Object font, int pixel_size, + char *name, int bytes)); +extern void register_font_driver P_ ((struct font_driver *driver, FRAME_PTR f)); +extern void free_font_driver_list P_ ((FRAME_PTR f)); + +extern struct font *font_prepare_composition P_ ((struct composition *cmp)); + + +#ifdef HAVE_LIBOTF +/* This can be used as `otf_capability' method of a font-driver. */ +extern Lisp_Object font_otf_capability P_ ((struct font *font)); +/* This can be used as `otf_gsub' method of a font-driver. */ +extern int font_otf_gsub P_ ((struct font *font, Lisp_Object gsub_spec, + Lisp_Object gstring_in, int from, int to, + Lisp_Object gstring_out, int idx)); +/* This can be used as `otf_gpos' method of a font-driver. */ +extern int font_otf_gpos P_ ((struct font *font, Lisp_Object gpos_spec, + Lisp_Object gstring, int from, int to)); +#endif /* HAVE_LIBOTF */ + +#ifdef HAVE_FREETYPE +extern struct font_driver ftfont_driver; +#endif /* HAVE_FREETYPE */ +#ifdef HAVE_X_WINDOWS +extern struct font_driver xfont_driver; +extern struct font_driver ftxfont_driver; +#ifdef HAVE_XFT +extern struct font_driver xftfont_driver; +#endif /* HAVE_XFT */ +#endif /* HAVE_X_WINDOWS */ +#ifdef WINDOWSNT +extern struct font_driver w32font_driver; +#endif /* WINDOWSNT */ +#ifdef MAC_OS +extern struct font_driver atmfont_driver; +#endif /* MAC_OS */ + +#endif /* not EMACS_FONT_H */ diff --git a/src/ftfont.c b/src/ftfont.c new file mode 100644 index 0000000000..fff8dd72a4 --- /dev/null +++ b/src/ftfont.c @@ -0,0 +1,731 @@ +/* ftfont.c -- FreeType font driver. + Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H13PRO009 + +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 2, 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; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_SIZES_H +#include +#include + +#include "lisp.h" +#include "dispextern.h" +#include "frame.h" +#include "blockinput.h" +#include "character.h" +#include "charset.h" +#include "coding.h" +#include "fontset.h" +#include "font.h" + +Lisp_Object Qfreetype; + +static int fc_initialized; +static FT_Library ft_library; + +static Lisp_Object freetype_font_cache; + +static Lisp_Object Qiso8859_1, Qiso10646_1, Qunicode_bmp; + +static FcCharSet *cs_iso8859_1; + +/* The actual structure for FreeType font that can be casted to struct + font. */ + +struct ftfont_info +{ + struct font font; + FT_Size ft_size; +}; + +static int +ftfont_build_basic_charsets () +{ + FcChar32 c; + + cs_iso8859_1 = FcCharSetCreate (); + if (! cs_iso8859_1) + return -1; + for (c = ' '; c < 127; c++) + if (! FcCharSetAddChar (cs_iso8859_1, c)) + return -1; + for (c = 192; c < 256; c++) + if (! FcCharSetAddChar (cs_iso8859_1, c)) + return -1; + return 0; +} + +static Lisp_Object ftfont_get_cache P_ ((Lisp_Object)); +static int ftfont_parse_name P_ ((FRAME_PTR, char *, Lisp_Object)); +static Lisp_Object ftfont_list P_ ((Lisp_Object, Lisp_Object)); +static Lisp_Object ftfont_list_family P_ ((Lisp_Object)); +static void ftfont_free_entity P_ ((Lisp_Object)); +static struct font *ftfont_open P_ ((FRAME_PTR, Lisp_Object, int)); +static void ftfont_close P_ ((FRAME_PTR, struct font *)); +static int ftfont_has_char P_ ((Lisp_Object, int)); +static unsigned ftfont_encode_char P_ ((struct font *, int)); +static int ftfont_text_extents P_ ((struct font *, unsigned *, int, + struct font_metrics *)); +static int ftfont_get_bitmap P_ ((struct font *, unsigned, + struct font_bitmap *, int)); +static int ftfont_anchor_point P_ ((struct font *, unsigned, int, + int *, int *)); + +struct font_driver ftfont_driver = + { + (Lisp_Object) NULL, /* Qfreetype */ + ftfont_get_cache, + ftfont_parse_name, + ftfont_list, + ftfont_list_family, + ftfont_free_entity, + ftfont_open, + ftfont_close, + /* We can't draw a text without device dependent functions. */ + NULL, + NULL, + ftfont_has_char, + ftfont_encode_char, + ftfont_text_extents, + /* We can't draw a text without device dependent functions. */ + NULL, + ftfont_get_bitmap, + NULL, + NULL, + NULL, + ftfont_anchor_point, +#ifdef HAVE_LIBOTF + font_otf_capability, + font_otf_gsub, + font_otf_gpos +#else + NULL, + NULL, + NULL +#endif /* HAVE_LIBOTF */ + }; + +#define SYMBOL_FcChar8(SYM) (FcChar8 *) SDATA (SYMBOL_NAME (SYM)) + +extern Lisp_Object QCname; + +static Lisp_Object +ftfont_get_cache (frame) + Lisp_Object frame; +{ + if (NILP (freetype_font_cache)) + freetype_font_cache = Fcons (Qt, Qnil); + return freetype_font_cache; +} + +static int +ftfont_parse_name (f, name, spec) + FRAME_PTR f; + char *name; + Lisp_Object spec; +{ + FcPattern *p; + FcChar8 *str; + int numeric; + double dbl; + + if (name[0] == '-' || strchr (name, '*')) + /* It seems that NAME is XLFD. */ + return -1; + p = FcNameParse ((FcChar8 *) name); + if (! p) + return -1; + if (FcPatternGetString (p, FC_FOUNDRY, 0, &str) == FcResultMatch) + ASET (spec, FONT_FOUNDRY_INDEX, + intern_downcase ((char *) str, strlen ((char *) str))); + if (FcPatternGetString (p, FC_FAMILY, 0, &str) == FcResultMatch) + ASET (spec, FONT_FAMILY_INDEX, + intern_downcase ((char *) str, strlen ((char *) str))); + if (FcPatternGetInteger (p, FC_WEIGHT, 0, &numeric) == FcResultMatch) + ASET (spec, FONT_WEIGHT_INDEX, make_number (numeric)); + if (FcPatternGetInteger (p, FC_SLANT, 0, &numeric) == FcResultMatch) + ASET (spec, FONT_SLANT_INDEX, make_number (numeric + 100)); + if (FcPatternGetInteger (p, FC_WIDTH, 0, &numeric) == FcResultMatch) + ASET (spec, FONT_WIDTH_INDEX, make_number (numeric)); + if (FcPatternGetDouble (p, FC_PIXEL_SIZE, 0, &dbl) == FcResultMatch) + ASET (spec, FONT_SIZE_INDEX, make_number (dbl)); + else if (FcPatternGetDouble (p, FC_SIZE, 0, &dbl) == FcResultMatch) + ASET (spec, FONT_SIZE_INDEX, make_float (dbl)); + return 0; +} + +static Lisp_Object +ftfont_list (frame, spec) + Lisp_Object frame, spec; +{ + Lisp_Object val, tmp, extra, font_name; + int i; + FcPattern *pattern = NULL; + FcCharSet *charset = NULL; + FcLangSet *langset = NULL; + FcFontSet *fontset = NULL; + FcObjectSet *objset = NULL; + Lisp_Object registry = Qnil; + + val = null_vector; + + if (! fc_initialized) + { + FcInit (); + fc_initialized = 1; + } + + if (! NILP (AREF (spec, FONT_ADSTYLE_INDEX))) + return val; + if (! NILP (AREF (spec, FONT_REGISTRY_INDEX))) + { + registry = AREF (spec, FONT_REGISTRY_INDEX); + if (EQ (registry, Qiso8859_1)) + { + if (! cs_iso8859_1 + && ftfont_build_basic_charsets () < 0) + goto err; + charset = cs_iso8859_1; + registry = Qnil; + } + } + + extra = AREF (spec, FONT_EXTRA_INDEX); + font_name = Qnil; + if (CONSP (extra)) + { + tmp = Fassq (QCotf, extra); + if (! NILP (tmp)) + return val; + tmp = Fassq (QClanguage, extra); + if (CONSP (tmp)) + { + langset = FcLangSetCreate (); + if (! langset) + goto err; + tmp = XCDR (tmp); + if (SYMBOLP (tmp)) + { + if (! FcLangSetAdd (langset, SYMBOL_FcChar8 (tmp))) + goto err; + } + else + while (CONSP (tmp)) + { + if (SYMBOLP (XCAR (tmp)) + && ! FcLangSetAdd (langset, SYMBOL_FcChar8 (XCAR (tmp)))) + goto err; + tmp = XCDR (tmp); + } + } + tmp = Fassq (QCname, extra); + if (CONSP (tmp)) + font_name = XCDR (tmp); + tmp = Fassq (QCscript, extra); + if (CONSP (tmp) && ! charset) + { + Lisp_Object script = XCDR (tmp); + Lisp_Object chars = assq_no_quit (script, + Vscript_representative_chars); + + if (CONSP (chars)) + { + charset = FcCharSetCreate (); + if (! charset) + goto err; + for (chars = XCDR (chars); CONSP (chars); chars = XCDR (chars)) + if (CHARACTERP (XCAR (chars)) + && ! FcCharSetAddChar (charset, XUINT (XCAR (chars)))) + goto err; + } + } + } + + if (! NILP (registry) && ! charset) + goto finish; + + if (STRINGP (font_name)) + { + if (! isalpha (SDATA (font_name)[0])) + goto finish; + pattern = FcNameParse (SDATA (font_name)); + if (! pattern) + goto err; + } + else + { + pattern = FcPatternCreate (); + if (! pattern) + goto err; + + tmp = AREF (spec, FONT_FOUNDRY_INDEX); + if (SYMBOLP (tmp) && ! NILP (tmp) + && ! FcPatternAddString (pattern, FC_FOUNDRY, SYMBOL_FcChar8 (tmp))) + goto err; + tmp = AREF (spec, FONT_FAMILY_INDEX); + if (SYMBOLP (tmp) && ! NILP (tmp) + && ! FcPatternAddString (pattern, FC_FAMILY, SYMBOL_FcChar8 (tmp))) + goto err; + tmp = AREF (spec, FONT_WEIGHT_INDEX); + if (INTEGERP (tmp) + && ! FcPatternAddInteger (pattern, FC_WEIGHT, XINT (tmp))) + goto err; + tmp = AREF (spec, FONT_SLANT_INDEX); + if (INTEGERP (tmp) + && XINT (tmp) >= 100 + && ! FcPatternAddInteger (pattern, FC_SLANT, XINT (tmp) - 100)) + goto err; + tmp = AREF (spec, FONT_WIDTH_INDEX); + if (INTEGERP (tmp) + && ! FcPatternAddInteger (pattern, FC_WIDTH, XINT (tmp))) + goto err; + if (! FcPatternAddBool (pattern, FC_SCALABLE, FcTrue)) + goto err; + } + + if (charset + && ! FcPatternAddCharSet (pattern, FC_CHARSET, charset)) + goto err; + if (langset + && ! FcPatternAddLangSet (pattern, FC_LANG, langset)) + goto err; + objset = FcObjectSetBuild (FC_FOUNDRY, FC_FAMILY, FC_WEIGHT, FC_SLANT, + FC_WIDTH, FC_PIXEL_SIZE, FC_SPACING, + FC_CHARSET, FC_FILE, NULL); + if (! objset) + goto err; + + BLOCK_INPUT; + fontset = FcFontList (NULL, pattern, objset); + UNBLOCK_INPUT; + if (! fontset) + goto err; + val = Qnil; + for (i = 0; i < fontset->nfont; i++) + { + FcPattern *p = fontset->fonts[i]; + FcChar8 *str, *file; + + if (FcPatternGetString (p, FC_FILE, 0, &file) == FcResultMatch + && FcPatternGetCharSet (p, FC_CHARSET, 0, &charset) == FcResultMatch) + { + Lisp_Object entity = Fmake_vector (make_number (FONT_ENTITY_MAX), + null_string); + int numeric; + double dbl; + FcPattern *p0; + + ASET (entity, FONT_TYPE_INDEX, Qfreetype); + ASET (entity, FONT_REGISTRY_INDEX, Qiso10646_1); + ASET (entity, FONT_FRAME_INDEX, frame); + ASET (entity, FONT_OBJLIST_INDEX, Qnil); + + if (FcPatternGetString (p, FC_FOUNDRY, 0, &str) == FcResultMatch) + ASET (entity, FONT_FOUNDRY_INDEX, + intern_downcase ((char *) str, strlen ((char *) str))); + if (FcPatternGetString (p, FC_FAMILY, 0, &str) == FcResultMatch) + ASET (entity, FONT_FAMILY_INDEX, + intern_downcase ((char *) str, strlen ((char *) str))); + if (FcPatternGetInteger (p, FC_WEIGHT, 0, &numeric) == FcResultMatch) + ASET (entity, FONT_WEIGHT_INDEX, make_number (numeric)); + if (FcPatternGetInteger (p, FC_SLANT, 0, &numeric) == FcResultMatch) + ASET (entity, FONT_SLANT_INDEX, make_number (numeric + 100)); + if (FcPatternGetInteger (p, FC_WIDTH, 0, &numeric) == FcResultMatch) + ASET (entity, FONT_WIDTH_INDEX, make_number (numeric)); + if (FcPatternGetDouble (p, FC_PIXEL_SIZE, 0, &dbl) == FcResultMatch) + ASET (entity, FONT_SIZE_INDEX, make_number (dbl)); + else + ASET (entity, FONT_SIZE_INDEX, make_number (0)); + + if (FcPatternGetInteger (p, FC_SPACING, 0, &numeric) != FcResultMatch) + numeric = FC_MONO; + p0 = FcPatternCreate (); + if (! p0 + || FcPatternAddString (p0, FC_FILE, file) == FcFalse + || FcPatternAddCharSet (p0, FC_CHARSET, charset) == FcFalse + || FcPatternAddInteger (p0, FC_SPACING, numeric) == FcFalse) + break; + ASET (entity, FONT_EXTRA_INDEX, make_save_value (p0, 0)); + + val = Fcons (entity, val); + } + } + val = Fvconcat (1, &val); + goto finish; + + err: + /* We come here because of unexpected error in fontconfig API call + (usually insufficiency memory). */ + val = Qnil; + + finish: + if (charset && charset != cs_iso8859_1) FcCharSetDestroy (charset); + if (objset) FcObjectSetDestroy (objset); + if (fontset) FcFontSetDestroy (fontset); + if (langset) FcLangSetDestroy (langset); + if (pattern) FcPatternDestroy (pattern); + + return val; +} + +static Lisp_Object +ftfont_list_family (frame) + Lisp_Object frame; +{ + Lisp_Object list; + FcPattern *pattern = NULL; + FcFontSet *fontset = NULL; + FcObjectSet *objset = NULL; + int i; + + if (! fc_initialized) + { + FcInit (); + fc_initialized = 1; + } + + pattern = FcPatternCreate (); + if (! pattern) + goto finish; + objset = FcObjectSetBuild (FC_FAMILY); + if (! objset) + goto finish; + fontset = FcFontList (NULL, pattern, objset); + if (! fontset) + goto finish; + + list = Qnil; + for (i = 0; i < fontset->nfont; i++) + { + FcPattern *pat = fontset->fonts[i]; + FcChar8 *str; + + if (FcPatternGetString (pat, FC_FAMILY, 0, &str) == FcResultMatch) + list = Fcons (intern_downcase ((char *) str, strlen ((char *) str)), + list); + } + + finish: + if (objset) FcObjectSetDestroy (objset); + if (fontset) FcFontSetDestroy (fontset); + if (pattern) FcPatternDestroy (pattern); + + return list; +} + + +static void +ftfont_free_entity (entity) + Lisp_Object entity; +{ + Lisp_Object val = AREF (entity, FONT_EXTRA_INDEX); + FcPattern *pattern = XSAVE_VALUE (val)->pointer; + + FcPatternDestroy (pattern); +} + +static struct font * +ftfont_open (f, entity, pixel_size) + FRAME_PTR f; + Lisp_Object entity; + int pixel_size; +{ + struct ftfont_info *ftfont_info; + struct font *font; + FT_Face ft_face; + FT_Size ft_size; + FT_UInt size; + Lisp_Object val; + FcPattern *pattern; + FcChar8 *file; + int spacing; + + val = AREF (entity, FONT_EXTRA_INDEX); + if (XTYPE (val) != Lisp_Misc + || XMISCTYPE (val) != Lisp_Misc_Save_Value) + return NULL; + pattern = XSAVE_VALUE (val)->pointer; + if (XSAVE_VALUE (val)->integer == 0) + { + /* We have not yet created FT_Face for this font. */ + if (! ft_library + && FT_Init_FreeType (&ft_library) != 0) + return NULL; + if (FcPatternGetString (pattern, FC_FILE, 0, &file) != FcResultMatch) + return NULL; + if (FT_New_Face (ft_library, (char *) file, 0, &ft_face) != 0) + return NULL; + FcPatternAddFTFace (pattern, FC_FT_FACE, ft_face); + ft_size = ft_face->size; + } + else + { + if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &ft_face) + != FcResultMatch) + return NULL; + if (FT_New_Size (ft_face, &ft_size) != 0) + return NULL; + if (FT_Activate_Size (ft_size) != 0) + { + FT_Done_Size (ft_size); + return NULL; + } + } + + size = XINT (AREF (entity, FONT_SIZE_INDEX)); + if (size == 0) + size = pixel_size; + if (FT_Set_Pixel_Sizes (ft_face, size, size) != 0) + { + if (XSAVE_VALUE (val)->integer == 0) + FT_Done_Face (ft_face); + return NULL; + } + + ftfont_info = malloc (sizeof (struct ftfont_info)); + if (! ftfont_info) + return NULL; + ftfont_info->ft_size = ft_size; + + font = (struct font *) ftfont_info; + font->entity = entity; + font->pixel_size = size; + font->driver = &ftfont_driver; + font->font.name = font->font.full_name = NULL; + font->file_name = (char *) file; + font->font.size = ft_face->size->metrics.max_advance >> 6; + font->ascent = ft_face->size->metrics.ascender >> 6; + font->descent = - ft_face->size->metrics.descender >> 6; + font->font.height = ft_face->size->metrics.height >> 6; + if (FcPatternGetInteger (pattern, FC_SPACING, 0, &spacing) != FcResultMatch + || spacing != FC_PROPORTIONAL) + font->font.average_width = font->font.space_width = font->font.size; + else + { + int i; + + for (i = 32; i < 127; i++) + { + if (FT_Load_Char (ft_face, i, FT_LOAD_DEFAULT) != 0) + break; + if (i == 32) + font->font.space_width = ft_face->glyph->metrics.horiAdvance >> 6; + font->font.average_width += ft_face->glyph->metrics.horiAdvance >> 6; + } + if (i == 127) + { + /* The font contains all ASCII printable characters. */ + font->font.average_width /= 95; + } + else + { + if (i == 32) + font->font.space_width = font->font.size; + font->font.average_width = font->font.size; + } + } + + font->font.baseline_offset = 0; + font->font.relative_compose = 0; + font->font.default_ascent = 0; + font->font.vertical_centering = 0; + + (XSAVE_VALUE (val)->integer)++; + + return font; +} + +static void +ftfont_close (f, font) + FRAME_PTR f; + struct font *font; +{ + struct ftfont_info *ftfont_info = (struct ftfont_info *) font; + Lisp_Object entity = font->entity; + Lisp_Object val = AREF (entity, FONT_EXTRA_INDEX); + + (XSAVE_VALUE (val)->integer)--; + if (XSAVE_VALUE (val)->integer == 0) + FT_Done_Face (ftfont_info->ft_size->face); + else + FT_Done_Size (ftfont_info->ft_size); + + free (font); +} + +static int +ftfont_has_char (entity, c) + Lisp_Object entity; + int c; +{ + Lisp_Object val; + FcPattern *pattern; + FcCharSet *charset; + + val = AREF (entity, FONT_EXTRA_INDEX); + pattern = XSAVE_VALUE (val)->pointer; + FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset); + + return (FcCharSetHasChar (charset, (FcChar32) c) == FcTrue); +} + +static unsigned +ftfont_encode_char (font, c) + struct font *font; + int c; +{ + struct ftfont_info *ftfont_info = (struct ftfont_info *) font; + FT_Face ft_face = ftfont_info->ft_size->face; + FT_ULong charcode = c; + FT_UInt code = FT_Get_Char_Index (ft_face, charcode); + + return (code > 0 ? code : 0xFFFFFFFF); +} + +static int +ftfont_text_extents (font, code, nglyphs, metrics) + struct font *font; + unsigned *code; + int nglyphs; + struct font_metrics *metrics; +{ + struct ftfont_info *ftfont_info = (struct ftfont_info *) font; + FT_Face ft_face = ftfont_info->ft_size->face; + int width = 0; + int i; + + if (ftfont_info->ft_size != ft_face->size) + FT_Activate_Size (ftfont_info->ft_size); + if (metrics) + bzero (metrics, sizeof (struct font_metrics)); + for (i = 0; i < nglyphs; i++) + { + if (FT_Load_Glyph (ft_face, code[i], FT_LOAD_DEFAULT) == 0) + { + FT_Glyph_Metrics *m = &ft_face->glyph->metrics; + + if (metrics) + { + if (metrics->lbearing > width + (m->horiBearingX >> 6)) + metrics->lbearing = width + (m->horiBearingX >> 6); + if (metrics->rbearing + < width + ((m->horiBearingX + m->width) >> 6)) + metrics->rbearing + = width + ((m->horiBearingX + m->width) >> 6); + if (metrics->ascent < (m->horiBearingY >> 6)) + metrics->ascent = m->horiBearingY >> 6; + if (metrics->descent > ((m->horiBearingY + m->height) >> 6)) + metrics->descent = (m->horiBearingY + m->height) >> 6; + } + width += m->horiAdvance >> 6; + } + else + { + width += font->font.space_width; + } + } + if (metrics) + metrics->width = width; + + return width; +} + +static int +ftfont_get_bitmap (font, code, bitmap, bits_per_pixel) + struct font *font; + unsigned code; + struct font_bitmap *bitmap; + int bits_per_pixel; +{ + struct ftfont_info *ftfont_info = (struct ftfont_info *) font; + FT_Face ft_face = ftfont_info->ft_size->face; + FT_Int32 load_flags = FT_LOAD_RENDER; + + if (ftfont_info->ft_size != ft_face->size) + FT_Activate_Size (ftfont_info->ft_size); + if (bits_per_pixel == 1) + { +#ifdef FT_LOAD_TARGET_MONO + load_flags |= FT_LOAD_TARGET_MONO; +#else + load_flags |= FT_LOAD_MONOCHROME; +#endif + } + else if (bits_per_pixel != 8) + /* We don't support such a rendering. */ + return -1; + + if (FT_Load_Glyph (ft_face, code, load_flags) != 0) + return -1; + bitmap->rows = ft_face->glyph->bitmap.rows; + bitmap->width = ft_face->glyph->bitmap.width; + bitmap->pitch = ft_face->glyph->bitmap.pitch; + bitmap->buffer = ft_face->glyph->bitmap.buffer; + bitmap->left = ft_face->glyph->bitmap_left; + bitmap->top = ft_face->glyph->bitmap_top; + bitmap->advance = ft_face->glyph->metrics.horiAdvance >> 6; + bitmap->extra = NULL; + + return 0; +} + +static int +ftfont_anchor_point (font, code, index, x, y) + struct font *font; + unsigned code; + int index; + int *x, *y; +{ + struct ftfont_info *ftfont_info = (struct ftfont_info *) font; + FT_Face ft_face = ftfont_info->ft_size->face; + + if (ftfont_info->ft_size != ft_face->size) + FT_Activate_Size (ftfont_info->ft_size); + if (FT_Load_Glyph (ft_face, code, FT_LOAD_DEFAULT) != 0) + return -1; + if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return -1; + if (index >= ft_face->glyph->outline.n_points) + return -1; + *x = ft_face->glyph->outline.points[index].x; + *y = ft_face->glyph->outline.points[index].y; + return 0; +} + + +void +syms_of_ftfont () +{ + staticpro (&freetype_font_cache); + freetype_font_cache = Qnil; + + DEFSYM (Qfreetype, "freetype"); + DEFSYM (Qiso8859_1, "iso8859-1"); + DEFSYM (Qiso10646_1, "iso10646-1"); + DEFSYM (Qunicode_bmp, "unicode-bmp"); + + ftfont_driver.type = Qfreetype; + register_font_driver (&ftfont_driver, NULL); +} diff --git a/src/ftxfont.c b/src/ftxfont.c new file mode 100644 index 0000000000..af6a96f706 --- /dev/null +++ b/src/ftxfont.c @@ -0,0 +1,346 @@ +/* ftxfont.c -- FreeType font driver on X (without using XFT). + Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H13PRO009 + +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 2, 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; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include +#include +#include + +#include "lisp.h" +#include "dispextern.h" +#include "xterm.h" +#include "frame.h" +#include "blockinput.h" +#include "character.h" +#include "charset.h" +#include "fontset.h" +#include "font.h" + +/* FTX font driver. */ + +static Lisp_Object Qftx; + +/* Prototypes for helper function. */ +static int ftxfont_draw_bitmap P_ ((FRAME_PTR, GC *, struct font *, unsigned, + int, int, XPoint *, int, int *n)); +static void ftxfont_draw_backgrond P_ ((FRAME_PTR, struct font *, GC, + int, int, int)); + +static int +ftxfont_draw_bitmap (f, gc, font, code, x, y, p, size, n) + FRAME_PTR f; + GC *gc; + struct font *font; + unsigned code; + int x, y; + XPoint *p; + int size, *n; +{ + struct font_bitmap bitmap; + unsigned char *b; + int i, j; + + if (ftfont_driver.get_bitmap (font, code, &bitmap, 1) < 0) + return 0; + for (i = 0, b = bitmap.buffer; i < bitmap.rows; + i++, b += bitmap.pitch) + { + if (size > 0x100) + { + for (j = 0; j < bitmap.width; j++) + if (b[j / 8] & (1 << (7 - (j % 8)))) + { + p[n[0]].x = x + bitmap.left + j; + p[n[0]].y = y - bitmap.top + i; + if (++n[0] == 0x400) + { + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + gc[0], p, size, CoordModeOrigin); + n[0] = 0; + } + } + } + else + { + for (j = 0; j < bitmap.width; j++) + { + int idx = (b[j] >> 5) - 1; + + if (idx >= 0) + { + XPoint *pp = p + size * idx; + + pp[n[idx]].x = x + bitmap.left + j; + pp[n[idx]].y = y - bitmap.top + i; + if (++(n[idx]) == 0x100) + { + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + gc[idx], pp, size, CoordModeOrigin); + n[idx] = 0; + } + } + } + } + } + + if (ftfont_driver.free_bitmap) + ftfont_driver.free_bitmap (font, &bitmap); + + return bitmap.advance; +} + +static void +ftxfont_draw_backgrond (f, font, gc, x, y, width) + FRAME_PTR f; + struct font *font; + GC gc; + int x, y, width; +{ + XGCValues xgcv; + + XGetGCValues (FRAME_X_DISPLAY (f), gc, + GCForeground | GCBackground, &xgcv); + XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background); + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc, + x, y - font->ascent, width, font->font.height); + XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground); +} + +/* Prototypes for font-driver methods. */ +static Lisp_Object ftxfont_list P_ ((Lisp_Object, Lisp_Object)); +static struct font *ftxfont_open P_ ((FRAME_PTR, Lisp_Object, int)); +static void ftxfont_close P_ ((FRAME_PTR, struct font *)); +static int ftxfont_prepare_face (FRAME_PTR, struct face *); +static void ftxfont_done_face (FRAME_PTR, struct face *); + +static int ftxfont_draw P_ ((struct glyph_string *, int, int, int, int, int)); + +struct font_driver ftxfont_driver; + +static Lisp_Object +ftxfont_list (frame, spec) + Lisp_Object frame; + Lisp_Object spec; +{ + Lisp_Object val = ftfont_driver.list (frame, spec); + + if (! NILP (val)) + { + int i; + + for (i = 0; i < ASIZE (val); i++) + ASET (AREF (val, i), FONT_TYPE_INDEX, Qftx); + } + return val; +} + +static struct font * +ftxfont_open (f, entity, pixel_size) + FRAME_PTR f; + Lisp_Object entity; + int pixel_size; +{ + Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + struct font *font; + XFontStruct *xfont = malloc (sizeof (XFontStruct)); + + if (! xfont) + return NULL; + font = ftfont_driver.open (f, entity, pixel_size); + if (! font) + { + free (xfont); + return NULL; + } + + xfont->fid = FRAME_FONT (f)->fid; + xfont->ascent = font->ascent; + xfont->descent = font->descent; + xfont->max_bounds.width = font->font.size; + xfont->min_bounds.width = font->min_width; + font->font.font = xfont; + font->driver = &ftxfont_driver; + + dpyinfo->n_fonts++; + + /* Set global flag fonts_changed_p to non-zero if the font loaded + has a character with a smaller width than any other character + before, or if the font loaded has a smaller height than any other + font loaded before. If this happens, it will make a glyph matrix + reallocation necessary. */ + if (dpyinfo->n_fonts == 1) + { + dpyinfo->smallest_font_height = font->font.height; + dpyinfo->smallest_char_width = font->min_width; + fonts_changed_p = 1; + } + else + { + if (dpyinfo->smallest_font_height > font->font.height) + dpyinfo->smallest_font_height = font->font.height, fonts_changed_p |= 1; + if (dpyinfo->smallest_char_width > font->min_width) + dpyinfo->smallest_char_width = font->min_width, fonts_changed_p |= 1; + } + + return font; +} + +static void +ftxfont_close (f, font) + FRAME_PTR f; + struct font *font; +{ + ftfont_driver.close (f, font); + FRAME_X_DISPLAY_INFO (f)->n_fonts--; +} + +static int +ftxfont_prepare_face (f, face) + FRAME_PTR f; + struct face *face; +{ + GC gc[6]; + XColor colors[3]; + XGCValues xgcv; + unsigned long mask = GCForeground | GCBackground | GCGraphicsExposures; + int i; + + face->extra = NULL; + + /* Here, we create 6 more GCs to simulate anti-aliasing. */ + BLOCK_INPUT; + XGetGCValues (FRAME_X_DISPLAY (f), face->gc, mask, &xgcv); + colors[0].pixel = face->foreground; + colors[1].pixel = face->background; + XQueryColors (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), colors, 2); + for (i = 1; i < 7; i++) + { + colors[2].red = (colors[0].red * i + colors[1].red * (7 - i)) / 7; + colors[2].green = (colors[0].green * i + colors[1].green * (7 - i)) / 7; + colors[2].blue = (colors[0].blue * i + colors[1].blue * (7 - i)) / 7; + if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &colors[2])) + break; + xgcv.foreground = colors[2].pixel; + gc[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + mask, &xgcv); + } + UNBLOCK_INPUT; + + if (i < 7) + return -1; + face->extra = malloc (sizeof (GC) * 7); + if (! face->extra) + return -1; + for (i = 0; i < 6; i++) + ((GC *) face->extra)[i] = gc[i]; + ((GC *) face->extra)[i] = face->gc; + return 0; +} + +static void +ftxfont_done_face (f, face) + FRAME_PTR f; + struct face *face; +{ + if (face->extra) + { + int i; + + BLOCK_INPUT; + for (i = 0; i < 7; i++) + XFreeGC (FRAME_X_DISPLAY (f), ((GC *) face->extra)[i]); + UNBLOCK_INPUT; + free (face->extra); + face->extra = NULL; + } +} + +static int +ftxfont_draw (s, from, to, x, y, with_background) + struct glyph_string *s; + int from, to, x, y, with_background; +{ + FRAME_PTR f = s->f; + struct face *face = s->face; + struct font *font = (struct font *) face->font; + XPoint p[0x700]; + int n[7]; + unsigned *code; + int len = to - from; + int i; + + n[0] = n[1] = n[2] = n[3] = n[4] = n[5] = n[6] = 0; + + BLOCK_INPUT; + + if (with_background) + ftxfont_draw_backgrond (f, font, s->gc, x, y, s->width); + code = alloca (sizeof (unsigned) * len); + for (i = 0; i < len; i++) + code[i] = ((XCHAR2B_BYTE1 (s->char2b + from + i) << 8) + | XCHAR2B_BYTE2 (s->char2b + from + i)); + + if (! face->extra) + { + for (i = 0; i < len; i++) + x += ftxfont_draw_bitmap (f, &face->gc, font, code[i], x, y, + p, 0x700, n); + if (n[0] > 0) + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + face->gc, p, n[0], CoordModeOrigin); + } + else + { + GC *gc = face->extra; + + for (i = 0; i < len; i++) + x += ftxfont_draw_bitmap (f, &face->gc, font, code[i], x, y, + p, 0x100, n); + for (i = 0; i < 7; i++) + if (n[i] > 0) + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + gc[i], p + 0x100 * i, n[i], CoordModeOrigin); + } + + UNBLOCK_INPUT; + + return len; +} + + + +void +syms_of_ftxfont () +{ + DEFSYM (Qftx, "ftx"); + + ftxfont_driver = ftfont_driver; + ftxfont_driver.type = Qftx; + ftxfont_driver.list = ftxfont_list; + ftxfont_driver.open = ftxfont_open; + ftxfont_driver.close = ftxfont_close; + ftxfont_driver.prepare_face = ftxfont_prepare_face; + ftxfont_driver.done_face = ftxfont_done_face; + ftxfont_driver.draw = ftxfont_draw; + + register_font_driver (&ftxfont_driver, NULL); +} diff --git a/src/xfont.c b/src/xfont.c new file mode 100644 index 0000000000..0d5d2f70b7 --- /dev/null +++ b/src/xfont.c @@ -0,0 +1,868 @@ +/* xfont.c -- X core font driver. + Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H13PRO009 + +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 2, 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; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include +#include +#include + +#include "lisp.h" +#include "dispextern.h" +#include "xterm.h" +#include "frame.h" +#include "blockinput.h" +#include "character.h" +#include "charset.h" +#include "fontset.h" +#include "font.h" + + +/* X core font driver. */ + +Lisp_Object Qx; + +/* Alist of font registry symbol and the corresponding charsets + information. The information is retrieved from + Vfont_encoding_alist on demand. + + Eash element has the form: + (REGISTRY . (ENCODING-CHARSET-ID . REPERTORY-CHARSET-ID)) + or + (REGISTRY . nil) + + In the former form, ENCODING-CHARSET-ID is an ID of a charset that + encodes a character code to a glyph code of a font, and + REPERTORY-CHARSET-ID is an ID of a charset that tells if a + character is supported by a font. + + The latter form means that the information for REGISTRY couldn't be + retrieved. */ +static Lisp_Object x_font_charset_alist; + +/* Prototypes of support functions. */ +extern void x_clear_errors P_ ((Display *)); + +static char *xfont_query_font P_ ((Display *, char *, Lisp_Object)); +static XCharStruct *xfont_get_pcm P_ ((XFontStruct *, XChar2b *)); +static int xfont_registry_charsets P_ ((Lisp_Object, struct charset **, + struct charset **)); + +static char * +xfont_query_font (display, name, spec) + Display *display; + char *name; + Lisp_Object spec; +{ + XFontStruct *font; + + BLOCK_INPUT; + x_catch_errors (display); + font = XLoadQueryFont (display, name); + name = NULL; + if (x_had_errors_p (display)) + { + /* This error is perhaps due to insufficient memory on X + server. Let's just ignore it. */ + x_clear_errors (display); + } + else if (font) + { + unsigned long value; + + if (XGetFontProperty (font, XA_FONT, &value)) + { + char *n = (char *) XGetAtomName (display, (Atom) value); + + if (font_parse_xlfd (n, spec, 0) >= 0) + name = n; + else + XFree (n); + } + XFreeFont (display, font); + } + x_uncatch_errors (); + UNBLOCK_INPUT; + + return name; +} + + +/* Get metrics of character CHAR2B in XFONT. Value is null if CHAR2B + is not contained in the font. */ + +static XCharStruct * +xfont_get_pcm (xfont, char2b) + XFontStruct *xfont; + XChar2b *char2b; +{ + /* The result metric information. */ + XCharStruct *pcm = NULL; + + xassert (xfont && char2b); + + if (xfont->per_char != NULL) + { + if (xfont->min_byte1 == 0 && xfont->max_byte1 == 0) + { + /* min_char_or_byte2 specifies the linear character index + corresponding to the first element of the per_char array, + max_char_or_byte2 is the index of the last character. A + character with non-zero CHAR2B->byte1 is not in the font. + A character with byte2 less than min_char_or_byte2 or + greater max_char_or_byte2 is not in the font. */ + if (char2b->byte1 == 0 + && char2b->byte2 >= xfont->min_char_or_byte2 + && char2b->byte2 <= xfont->max_char_or_byte2) + pcm = xfont->per_char + char2b->byte2 - xfont->min_char_or_byte2; + } + else + { + /* If either min_byte1 or max_byte1 are nonzero, both + min_char_or_byte2 and max_char_or_byte2 are less than + 256, and the 2-byte character index values corresponding + to the per_char array element N (counting from 0) are: + + byte1 = N/D + min_byte1 + byte2 = N\D + min_char_or_byte2 + + where: + + D = max_char_or_byte2 - min_char_or_byte2 + 1 + / = integer division + \ = integer modulus */ + if (char2b->byte1 >= xfont->min_byte1 + && char2b->byte1 <= xfont->max_byte1 + && char2b->byte2 >= xfont->min_char_or_byte2 + && char2b->byte2 <= xfont->max_char_or_byte2) + pcm = (xfont->per_char + + ((xfont->max_char_or_byte2 - xfont->min_char_or_byte2 + 1) + * (char2b->byte1 - xfont->min_byte1)) + + (char2b->byte2 - xfont->min_char_or_byte2)); + } + } + else + { + /* If the per_char pointer is null, all glyphs between the first + and last character indexes inclusive have the same + information, as given by both min_bounds and max_bounds. */ + if (char2b->byte2 >= xfont->min_char_or_byte2 + && char2b->byte2 <= xfont->max_char_or_byte2) + pcm = &xfont->max_bounds; + } + + return ((pcm == NULL + || (pcm->width == 0 && (pcm->rbearing - pcm->lbearing) == 0)) + ? NULL : pcm); +} + +extern Lisp_Object find_font_encoding P_ ((Lisp_Object)); + +/* Return encoding charset and repertory charset for REGISTRY in + ENCODING and REPERTORY correspondingly. If correct information for + REGISTRY is available, return 0. Otherwise return -1. */ + +static int +xfont_registry_charsets (registry, encoding, repertory) + Lisp_Object registry; + struct charset **encoding, **repertory; +{ + Lisp_Object val; + int encoding_id, repertory_id; + + val = assq_no_quit (registry, x_font_charset_alist); + if (! NILP (val)) + { + val = XCDR (val); + if (NILP (val)) + return -1; + encoding_id = XINT (XCAR (val)); + repertory_id = XINT (XCDR (val)); + } + else + { + val = find_font_encoding (SYMBOL_NAME (registry)); + if (SYMBOLP (val) && CHARSETP (val)) + { + encoding_id = repertory_id = XINT (CHARSET_SYMBOL_ID (val)); + } + else if (CONSP (val)) + { + if (! CHARSETP (XCAR (val))) + goto invalid_entry; + encoding_id = XINT (CHARSET_SYMBOL_ID (XCAR (val))); + if (NILP (XCDR (val))) + repertory_id = -1; + else + { + if (! CHARSETP (XCDR (val))) + goto invalid_entry; + repertory_id = XINT (CHARSET_SYMBOL_ID (XCDR (val))); + } + } + else + goto invalid_entry; + val = Fcons (make_number (encoding_id), make_number (repertory_id)); + x_font_charset_alist + = nconc2 (x_font_charset_alist, Fcons (Fcons (registry, val), Qnil)); + } + + if (encoding) + *encoding = CHARSET_FROM_ID (encoding_id); + if (repertory) + *repertory = repertory_id >= 0 ? CHARSET_FROM_ID (repertory_id) : NULL; + return 0; + + invalid_entry: + x_font_charset_alist + = nconc2 (x_font_charset_alist, Fcons (Fcons (registry, Qnil), Qnil)); + return -1; +} + +static Lisp_Object xfont_get_cache P_ ((Lisp_Object)); +static int xfont_parse_name P_ ((FRAME_PTR, char *, Lisp_Object)); +static Lisp_Object xfont_list P_ ((Lisp_Object, Lisp_Object)); +static Lisp_Object xfont_list_family P_ ((Lisp_Object)); +static struct font *xfont_open P_ ((FRAME_PTR, Lisp_Object, int)); +static void xfont_close P_ ((FRAME_PTR, struct font *)); +static int xfont_prepare_face P_ ((FRAME_PTR, struct face *)); +#if 0 +static void xfont_done_face P_ ((FRAME_PTR, struct face *)); +#endif +static int xfont_has_char P_ ((Lisp_Object, int)); +static unsigned xfont_encode_char P_ ((struct font *, int)); +static int xfont_text_extents P_ ((struct font *, unsigned *, int, + struct font_metrics *)); +static int xfont_draw P_ ((struct glyph_string *, int, int, int, int, int)); + +struct font_driver xfont_driver = + { + (Lisp_Object) NULL, /* Qx */ + xfont_get_cache, + xfont_parse_name, + xfont_list, + xfont_list_family, + NULL, + xfont_open, + xfont_close, + xfont_prepare_face, + NULL /*xfont_done_face*/, + xfont_has_char, + xfont_encode_char, + xfont_text_extents, + xfont_draw, + }; + +extern Lisp_Object QCname; + +static Lisp_Object +xfont_get_cache (frame) + Lisp_Object frame; +{ + Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (XFRAME (frame)); + + return (dpyinfo->name_list_element); +} + +static int +xfont_parse_name (f, name, spec) + FRAME_PTR f; + char *name; + Lisp_Object spec; +{ + if (font_parse_xlfd (name, spec, 0) >= 0) + return 0; + name = xfont_query_font (FRAME_X_DISPLAY (f), name, spec); + if (name) + { + XFree (name); + return 0; + } + return -1; +} + +extern Lisp_Object Vface_alternative_font_registry_alist; + +static Lisp_Object +xfont_list (frame, spec) + Lisp_Object frame, spec; +{ + FRAME_PTR f = XFRAME (frame); + Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Lisp_Object *vec, val, extra, font_name, entity; + char name[256], **names; + int i, idx, limit, num_fonts; + int error_occurred = 0; + USE_SAFE_ALLOCA; + + extra = AREF (spec, FONT_EXTRA_INDEX); + font_name = Qnil; + if (CONSP (extra)) + { + val = Fassq (QCotf, extra); + if (! NILP (val)) + return null_vector; + val = Fassq (QCname, extra); + if (CONSP (val)) + font_name = XCDR (val); + } + + if (! STRINGP (font_name) + && font_unparse_xlfd (spec, 0, name, 256) < 0) + return null_vector; + + BLOCK_INPUT; + x_catch_errors (dpyinfo->display); + + if (STRINGP (font_name)) + { + XFontStruct *font = XLoadQueryFont (dpyinfo->display, + (char *) SDATA (font_name)); + unsigned long value; + + num_fonts = 0; + if (x_had_errors_p (dpyinfo->display)) + { + /* This error is perhaps due to insufficient memory on X + server. Let's just ignore it. */ + font = NULL; + error_occurred = 1; + x_clear_errors (dpyinfo->display); + } + if (font) + { + if (XGetFontProperty (font, XA_FONT, &value)) + { + char *n = (char *) XGetAtomName (dpyinfo->display, (Atom) value); + int len = strlen (n); + char *tmp; + + /* If DXPC (a Differential X Protocol Compressor) + Ver.3.7 is running, XGetAtomName will return null + string. We must avoid such a name. */ + if (len > 0) + { + num_fonts = 1; + names = (char **) alloca (sizeof (char *)); + /* Some systems only allow alloca assigned to a + simple var. */ + tmp = (char *) alloca (len + 1); names[0] = tmp; + bcopy (n, names[0], len + 1); + } + XFree (n); + } + XFreeFont (dpyinfo->display, font); + } + } + else + { + Lisp_Object registry = AREF (spec, FONT_REGISTRY_INDEX); + Lisp_Object alter = Qnil; + char *r = NULL; + + if (! NILP (registry)) + alter = Fassoc_string (SYMBOL_NAME (registry), + Vface_alternative_font_registry_alist); + while (1) + { + for (limit = 512, num_fonts = 0; ; limit *= 2) + { + names = XListFonts (dpyinfo->display, name, limit, &num_fonts); + if (x_had_errors_p (dpyinfo->display)) + { + /* This error is perhaps due to insufficient memory + on X server. Let's just ignore it. */ + x_clear_errors (dpyinfo->display); + error_occurred = 1; + num_fonts = 0; + break; + } + if (num_fonts < limit) + break; + XFreeFontNames (names); + } + if (num_fonts > 0 + || NILP (alter)) + break; + + /* Setup for trying alternatives. */ + if (! r + && ! (r = strstr (name, (char *) SDATA (SYMBOL_NAME (registry))))) + abort (); + while (1) + { + registry = Qnil; + alter = XCDR (alter); + if (NILP (alter)) + break; + registry = XCAR (alter); + if ((r - name) + SBYTES (registry) < 255) + break; + } + if (NILP (registry)) + break; + bcopy (SDATA (registry), r, SBYTES (registry)); + } + } + + x_uncatch_errors (); + UNBLOCK_INPUT; + + if (error_occurred) + return Qnil; + if (num_fonts == 0) + return null_vector; + + entity = Fmake_vector (make_number (FONT_ENTITY_MAX), Qnil); + ASET (entity, FONT_TYPE_INDEX, Qx); + ASET (entity, FONT_FRAME_INDEX, frame); + + SAFE_ALLOCA_LISP (vec, num_fonts); + for (i = idx = 0; i < num_fonts; i++) + { + if (font_parse_xlfd (names[i], entity, 0) > 0) + vec[idx++] = Fcopy_sequence (entity); + } + if (! STRINGP (font_name)) + { + BLOCK_INPUT; + XFreeFontNames (names); + UNBLOCK_INPUT; + } + val = Fvector (idx, vec); + SAFE_FREE (); + + return val; +} + +static int +memq_no_quit (elt, list) + Lisp_Object elt, list; +{ + while (CONSP (list) && ! EQ (XCAR (list), elt)) + list = XCDR (list); + return (CONSP (list)); +} + +static Lisp_Object +xfont_list_family (frame) +{ + FRAME_PTR f = XFRAME (frame); + Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + char **names; + int num_fonts, i; + Lisp_Object list; + char *last_family; + int last_len; + + BLOCK_INPUT; + x_catch_errors (dpyinfo->display); + names = XListFonts (dpyinfo->display, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*", + 0x8000, &num_fonts); + if (x_had_errors_p (dpyinfo->display)) + { + /* This error is perhaps due to insufficient memory on X server. + Let's just ignore it. */ + x_clear_errors (dpyinfo->display); + num_fonts = 0; + } + + list = Qnil; + for (i = 0, last_len = 0; i < num_fonts; i++) + { + char *p0 = names[i], *p1; + Lisp_Object family; + + p0++; /* skip the leading '-' */ + while (*p0 && *p0 != '-') p0++; /* skip foundry */ + if (! *p0) + continue; + p1 = ++p0; + while (*p1 && *p1 != '-') p1++; /* find the end of family */ + if (! *p1 || p1 == p0) + continue; + if (last_len == p1 - p0 + && bcmp (last_family, p0, last_len) == 0) + continue; + last_len = p1 - p0; + last_family = p0; + family = intern_downcase (p0, last_len); + if (! memq_no_quit (family, list)) + list = Fcons (family, list); + } + + XFreeFontNames (names); + x_uncatch_errors (); + UNBLOCK_INPUT; + + return list; +} + +static struct font * +xfont_open (f, entity, pixel_size) + FRAME_PTR f; + Lisp_Object entity; + int pixel_size; +{ + Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display *display = dpyinfo->display; + char name[256]; + int len; + unsigned long value; + Lisp_Object registry; + struct charset *encoding, *repertory; + struct font *font; + XFontStruct *xfont; + + /* At first, check if we know how to encode characters for this + font. */ + registry = AREF (entity, FONT_REGISTRY_INDEX); + if (xfont_registry_charsets (registry, &encoding, &repertory) < 0) + return NULL; + + if (XINT (AREF (entity, FONT_SIZE_INDEX)) != 0) + pixel_size = XINT (AREF (entity, FONT_SIZE_INDEX)); + len = font_unparse_xlfd (entity, pixel_size, name, 256); + if (len <= 0) + return NULL; + + BLOCK_INPUT; + x_catch_errors (display); + xfont = XLoadQueryFont (display, name); + if (x_had_errors_p (display)) + { + /* This error is perhaps due to insufficient memory on X server. + Let's just ignore it. */ + x_clear_errors (display); + xfont = NULL; + } + x_uncatch_errors (); + UNBLOCK_INPUT; + + if (! xfont) + return NULL; + font = malloc (sizeof (struct font)); + font->font.font = xfont; + font->entity = entity; + font->pixel_size = pixel_size; + font->driver = &xfont_driver; + font->font.name = malloc (len + 1); + if (! font->font.name) + { + XFreeFont (display, xfont); + free (font); + return NULL; + } + bcopy (name, font->font.name, len + 1); + font->font.charset = encoding->id; + font->encoding_charset = encoding->id; + font->repertory_charet = repertory ? repertory->id : -1; + font->ascent = xfont->ascent; + font->descent = xfont->descent; + + if (xfont->min_bounds.width == xfont->max_bounds.width) + { + /* Fixed width font. */ + font->font.average_width = font->font.space_width + = xfont->min_bounds.width; + } + else + { + XChar2b char2b; + XCharStruct *pcm; + + char2b.byte1 = 0x00, char2b.byte2 = 0x20; + pcm = xfont_get_pcm (xfont, &char2b); + if (pcm) + font->font.space_width = pcm->width; + else + font->font.space_width = xfont->max_bounds.width; + + font->font.average_width + = (XGetFontProperty (xfont, dpyinfo->Xatom_AVERAGE_WIDTH, &value) + ? (long) value / 10 : 0); + if (font->font.average_width < 0) + font->font.average_width = - font->font.average_width; + if (font->font.average_width == 0) + { + if (pcm) + { + int width = pcm->width; + for (char2b.byte2 = 33; char2b.byte2 <= 126; char2b.byte2++) + if ((pcm = xfont_get_pcm (xfont, &char2b)) != NULL) + width += pcm->width; + font->font.average_width = width / 95; + } + else + font->font.average_width = xfont->max_bounds.width; + } + } + font->min_width = xfont->min_bounds.width; + if (font->min_width <= 0) + font->min_width = font->font.space_width; + + BLOCK_INPUT; + /* Try to get the full name of FONT. Put it in FULL_NAME. */ + if (XGetFontProperty (xfont, XA_FONT, &value)) + { + char *full_name = NULL, *p0, *p; + int dashes = 0; + + p0 = p = (char *) XGetAtomName (FRAME_X_DISPLAY (f), (Atom) value);; + /* Count the number of dashes in the "full name". + If it is too few, this isn't really the font's full name, + so don't use it. + In X11R4, the fonts did not come with their canonical names + stored in them. */ + while (*p) + { + if (*p == '-') + dashes++; + p++; + } + + if (dashes >= 13) + { + full_name = (char *) malloc (p - p0 + 1); + if (full_name) + bcopy (p0, full_name, p - p0 + 1); + } + XFree (p0); + + if (full_name) + font->font.full_name = full_name; + else + font->font.full_name = font->font.name; + } + font->file_name = NULL; + + font->font.size = xfont->max_bounds.width; + font->font.height = xfont->ascent + xfont->descent; + font->font.baseline_offset + = (XGetFontProperty (xfont, dpyinfo->Xatom_MULE_BASELINE_OFFSET, &value) + ? (long) value : 0); + font->font.relative_compose + = (XGetFontProperty (xfont, dpyinfo->Xatom_MULE_RELATIVE_COMPOSE, &value) + ? (long) value : 0); + font->font.default_ascent + = (XGetFontProperty (xfont, dpyinfo->Xatom_MULE_DEFAULT_ASCENT, &value) + ? (long) value : 0); + font->font.vertical_centering + = (STRINGP (Vvertical_centering_font_regexp) + && (fast_c_string_match_ignore_case + (Vvertical_centering_font_regexp, font->font.full_name) >= 0)); + + UNBLOCK_INPUT; + + dpyinfo->n_fonts++; + + /* Set global flag fonts_changed_p to non-zero if the font loaded + has a character with a smaller width than any other character + before, or if the font loaded has a smaller height than any other + font loaded before. If this happens, it will make a glyph matrix + reallocation necessary. */ + if (dpyinfo->n_fonts == 1) + { + dpyinfo->smallest_font_height = font->font.height; + dpyinfo->smallest_char_width = font->min_width; + fonts_changed_p = 1; + } + else + { + if (dpyinfo->smallest_font_height > font->font.height) + dpyinfo->smallest_font_height = font->font.height, fonts_changed_p |= 1; + if (dpyinfo->smallest_char_width > font->min_width) + dpyinfo->smallest_char_width = font->min_width, fonts_changed_p |= 1; + } + + return font; +} + +static void +xfont_close (f, font) + FRAME_PTR f; + struct font *font; +{ + BLOCK_INPUT; + XFreeFont (FRAME_X_DISPLAY (f), font->font.font); + UNBLOCK_INPUT; + + if (font->font.name != font->font.full_name) + free (font->font.full_name); + free (font->font.name); + free (font); + FRAME_X_DISPLAY_INFO (f)->n_fonts--; +} + +static int +xfont_prepare_face (f, face) + FRAME_PTR f; + struct face *face; +{ + BLOCK_INPUT; + XSetFont (FRAME_X_DISPLAY (f), face->gc, face->font->fid); + UNBLOCK_INPUT; + + return 0; +} + +#if 0 +static void +xfont_done_face (f, face) + FRAME_PTR f; + struct face *face; +{ + if (face->extra) + { + BLOCK_INPUT; + XFreeGC (FRAME_X_DISPLAY (f), (GC) face->extra); + UNBLOCK_INPUT; + face->extra = NULL; + } +} +#endif /* 0 */ + +static int +xfont_has_char (entity, c) + Lisp_Object entity; + int c; +{ + Lisp_Object registry = AREF (entity, FONT_REGISTRY_INDEX); + struct charset *repertory; + + if (xfont_registry_charsets (registry, NULL, &repertory) < 0) + return -1; + if (! repertory) + return -1; + return (ENCODE_CHAR (repertory, c) != CHARSET_INVALID_CODE (repertory)); +} + +static unsigned +xfont_encode_char (font, c) + struct font *font; + int c; +{ + struct charset *charset; + unsigned code; + XChar2b char2b; + + charset = CHARSET_FROM_ID (font->encoding_charset); + code = ENCODE_CHAR (charset, c); + if (code == CHARSET_INVALID_CODE (charset)) + return 0xFFFFFFFF; + if (font->repertory_charet >= 0) + { + charset = CHARSET_FROM_ID (font->repertory_charet); + return (ENCODE_CHAR (charset, c) != CHARSET_INVALID_CODE (charset) + ? code : 0xFFFFFFFF); + } + char2b.byte1 = code >> 16; + char2b.byte2 = code & 0xFFFF; + return (xfont_get_pcm (font->font.font, &char2b) ? code : 0xFFFFFFFF); +} + +static int +xfont_text_extents (font, code, nglyphs, metrics) + struct font *font; + unsigned *code; + int nglyphs; + struct font_metrics *metrics; +{ + int width = 0; + int i, x; + + if (metrics) + bzero (metrics, sizeof (struct font_metrics)); + for (i = 0, x = 0; i < nglyphs; i++) + { + XChar2b char2b; + static XCharStruct *pcm; + + if (code[i] >= 0x10000) + continue; + char2b.byte1 = code[i] >> 8, char2b.byte2 = code[i] & 0xFF; + pcm = xfont_get_pcm (font->font.font, &char2b); + if (! pcm) + continue; + if (metrics->lbearing > width + pcm->lbearing) + metrics->lbearing = width + pcm->lbearing; + if (metrics->rbearing < width + pcm->rbearing) + metrics->rbearing = width + pcm->rbearing; + if (metrics->ascent < pcm->ascent) + metrics->ascent = pcm->ascent; + if (metrics->descent < pcm->descent) + metrics->descent = pcm->descent; + width += pcm->width; + } + if (metrics) + metrics->width = width; + return width; +} + +static int +xfont_draw (s, from, to, x, y, with_background) + struct glyph_string *s; + int from, to, x, y, with_background; +{ + XFontStruct *xfont = s->face->font; + int len = to - from; + + if (xfont->min_byte1 == 0 && xfont->max_byte1 == 0) + { + char *str; + int i; + USE_SAFE_ALLOCA; + + SAFE_ALLOCA (str, char *, len); + for (i = 0; i < len ; i++) + str[i] = XCHAR2B_BYTE2 (s->char2b + from + i); + if (with_background > 0) + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + s->gc, x, y, str, len); + else + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + s->gc, x, y, str, len); + SAFE_FREE (); + return s->nchars; + } + + if (with_background > 0) + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + s->gc, x, y, s->char2b + from, len); + else + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + s->gc, x, y, s->char2b + from, len); + + return len; +} + + + +void +syms_of_xfont () +{ + staticpro (&x_font_charset_alist); + x_font_charset_alist = Qnil; + + DEFSYM (Qx, "x"); + xfont_driver.type = Qx; + register_font_driver (&xfont_driver, NULL); +} diff --git a/src/xftfont.c b/src/xftfont.c new file mode 100644 index 0000000000..dd2b95ccd1 --- /dev/null +++ b/src/xftfont.c @@ -0,0 +1,552 @@ +/* xftfont.c -- XFT font driver. + Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H13PRO009 + +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 2, 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; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include +#include +#include +#include + +#include "lisp.h" +#include "dispextern.h" +#include "xterm.h" +#include "frame.h" +#include "blockinput.h" +#include "character.h" +#include "charset.h" +#include "fontset.h" +#include "font.h" + +/* Xft font driver. */ + +static Lisp_Object Qxft; + +/* The actual structure for Xft font that can be casted to struct + font. */ + +struct xftfont_info +{ + struct font font; + Display *display; + int screen; + XftFont *xftfont; + FT_Face ft_face; +}; + +/* Structure pointed by (struct face *)->extra */ +struct xftface_info +{ + XftColor xft_fg; + XftColor xft_bg; + XftDraw *xft_draw; +}; + +static void xftfont_get_colors P_ ((FRAME_PTR, struct face *, GC gc, + struct xftface_info *, + XftColor *fg, XftColor *bg)); +static Font xftfont_default_fid P_ ((FRAME_PTR)); + + +/* Setup colors pointed by FG and BG for GC. If XFTFACE_INFO is not + NULL, reuse the colors in it if possible. BG may be NULL. */ +static void +xftfont_get_colors (f, face, gc, xftface_info, fg, bg) + FRAME_PTR f; + struct face *face; + GC gc; + struct xftface_info *xftface_info; + XftColor *fg, *bg; +{ + if (xftface_info && face->gc == gc) + { + *fg = xftface_info->xft_fg; + if (bg) + *bg = xftface_info->xft_bg; + } + else + { + XGCValues xgcv; + int fg_done = 0, bg_done = 0; + + BLOCK_INPUT; + XGetGCValues (FRAME_X_DISPLAY (f), gc, + GCForeground | GCBackground, &xgcv); + if (xftface_info) + { + if (xgcv.foreground == face->foreground) + *fg = xftface_info->xft_fg, fg_done = 1; + else if (xgcv.foreground == face->background) + *fg = xftface_info->xft_bg, fg_done = 1; + if (! bg) + bg_done = 1; + else if (xgcv.background == face->background) + *bg = xftface_info->xft_bg, bg_done = 1; + else if (xgcv.background == face->foreground) + *bg = xftface_info->xft_fg, bg_done = 1; + } + + if (fg_done + bg_done < 2) + { + XColor colors[2]; + + colors[0].pixel = fg->pixel = xgcv.foreground; + if (bg) + colors[1].pixel = bg->pixel = xgcv.background; + XQueryColors (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), colors, + bg ? 2 : 1); + fg->color.alpha = 0xFFFF; + fg->color.red = colors[0].red; + fg->color.green = colors[0].green; + fg->color.blue = colors[0].blue; + if (bg) + { + bg->color.alpha = 0xFFFF; + bg->color.red = colors[1].red; + bg->color.green = colors[1].green; + bg->color.blue = colors[1].blue; + } + } + UNBLOCK_INPUT; + } +} + +/* Return the default Font ID on frame F. */ + +static Font +xftfont_default_fid (f) + FRAME_PTR f; +{ + static int fid_known; + static Font fid; + + if (! fid_known) + { + fid = XLoadFont (FRAME_X_DISPLAY (f), "fixed"); + if (! fid) + { + fid = XLoadFont (FRAME_X_DISPLAY (f), "*"); + if (! fid) + abort (); + } + } + return fid; +} + + +static Lisp_Object xftfont_list P_ ((Lisp_Object, Lisp_Object)); +static struct font *xftfont_open P_ ((FRAME_PTR, Lisp_Object, int)); +static void xftfont_close P_ ((FRAME_PTR, struct font *)); +static int xftfont_prepare_face P_ ((FRAME_PTR, struct face *)); +static void xftfont_done_face P_ ((FRAME_PTR, struct face *)); +static unsigned xftfont_encode_char P_ ((struct font *, int)); +static int xftfont_text_extents P_ ((struct font *, unsigned *, int, + struct font_metrics *)); +static int xftfont_draw P_ ((struct glyph_string *, int, int, int, int, int)); + +static int xftfont_anchor_point P_ ((struct font *, unsigned, int, + int *, int *)); + +struct font_driver xftfont_driver; + +static Lisp_Object +xftfont_list (frame, spec) + Lisp_Object frame; + Lisp_Object spec; +{ + Lisp_Object val = ftfont_driver.list (frame, spec); + + if (! NILP (val)) + { + int i; + + for (i = 0; i < ASIZE (val); i++) + ASET (AREF (val, i), FONT_TYPE_INDEX, Qxft); + } + return val; +} + +static FcChar8 ascii_printable[95]; + +static struct font * +xftfont_open (f, entity, pixel_size) + FRAME_PTR f; + Lisp_Object entity; + int pixel_size; +{ + Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display *display = FRAME_X_DISPLAY (f); + Lisp_Object val; + FcPattern *pattern, *pat; + FcChar8 *file; + XFontStruct *xfont; + struct xftfont_info *xftfont_info; + struct font *font; + double size = 0; + XftFont *xftfont; + int spacing; + + val = AREF (entity, FONT_EXTRA_INDEX); + if (XTYPE (val) != Lisp_Misc + || XMISCTYPE (val) != Lisp_Misc_Save_Value) + return NULL; + pattern = XSAVE_VALUE (val)->pointer; + if (FcPatternGetString (pattern, FC_FILE, 0, &file) != FcResultMatch) + return NULL; + + size = XINT (AREF (entity, FONT_SIZE_INDEX)); + if (size == 0) + size = pixel_size; + pat = FcPatternCreate (); + FcPatternAddString (pat, FC_FILE, file); + FcPatternAddDouble (pat, FC_PIXEL_SIZE, pixel_size); + FcPatternAddBool (pat, FC_ANTIALIAS, FcTrue); + xftfont = XftFontOpenPattern (display, pat); + /* We should not destroy PAT here because it is kept in XFTFONT and + destroyed automatically when XFTFONT is closed. */ + if (! xftfont) + return NULL; + + xftfont_info = malloc (sizeof (struct xftfont_info)); + if (! xftfont_info) + { + XftFontClose (display, xftfont); + return NULL; + } + xfont = malloc (sizeof (XFontStruct)); + if (! xftfont_info) + { + XftFontClose (display, xftfont); + free (xftfont_info); + return NULL; + } + xftfont_info->display = display; + xftfont_info->screen = FRAME_X_SCREEN_NUMBER (f); + xftfont_info->xftfont = xftfont; + xftfont_info->ft_face = XftLockFace (xftfont); + + font = (struct font *) xftfont_info; + font->entity = entity; + font->pixel_size = size; + font->driver = &xftfont_driver; + font->font.name = font->font.full_name = NULL; + font->file_name = (char *) file; + font->font.size = xftfont->max_advance_width; + font->ascent = xftfont->ascent; + font->descent = xftfont->descent; + font->font.height = xftfont->ascent + xftfont->descent; + + if (FcPatternGetInteger (xftfont->pattern, FC_SPACING, 0, &spacing) + != FcResultMatch) + spacing = FC_PROPORTIONAL; + if (spacing != FC_PROPORTIONAL) + font->font.average_width = font->font.space_width + = xftfont->max_advance_width; + else + { + XGlyphInfo extents; + + if (! ascii_printable[0]) + { + int i; + for (i = 0; i < 95; i++) + ascii_printable[i] = ' ' + i; + } + XftTextExtents8 (display, xftfont, ascii_printable, 1, &extents); + font->font.space_width = extents.xOff; + if (font->font.space_width <= 0) + /* dirty workaround */ + font->font.space_width = pixel_size; + XftTextExtents8 (display, xftfont, ascii_printable + 1, 94, &extents); + font->font.average_width = (font->font.space_width + extents.xOff) / 95; + } + + /* Unfortunately Xft doesn't provide a way to get minimum char + width. So, we use space_width instead. */ + font->min_width = font->font.space_width; + + font->font.baseline_offset = 0; + font->font.relative_compose = 0; + font->font.default_ascent = 0; + font->font.vertical_centering = 0; + + /* Setup pseudo XFontStruct */ + xfont->fid = xftfont_default_fid (f); + xfont->ascent = xftfont->ascent; + xfont->descent = xftfont->descent; + xfont->max_bounds.descent = xftfont->descent; + xfont->max_bounds.width = xftfont->max_advance_width; + xfont->min_bounds.width = font->font.space_width; + font->font.font = xfont; + + dpyinfo->n_fonts++; + + /* Set global flag fonts_changed_p to non-zero if the font loaded + has a character with a smaller width than any other character + before, or if the font loaded has a smaller height than any other + font loaded before. If this happens, it will make a glyph matrix + reallocation necessary. */ + if (dpyinfo->n_fonts == 1) + { + dpyinfo->smallest_font_height = font->font.height; + dpyinfo->smallest_char_width = font->min_width; + fonts_changed_p = 1; + } + else + { + if (dpyinfo->smallest_font_height > font->font.height) + dpyinfo->smallest_font_height = font->font.height, + fonts_changed_p |= 1; + if (dpyinfo->smallest_char_width > font->min_width) + dpyinfo->smallest_char_width = font->min_width, + fonts_changed_p |= 1; + } + + return font; +} + +static void +xftfont_close (f, font) + FRAME_PTR f; + struct font *font; +{ + struct xftfont_info *xftfont_info = (struct xftfont_info *) font; + + XftUnlockFace (xftfont_info->xftfont); + XftFontClose (xftfont_info->display, xftfont_info->xftfont); + free (font); + FRAME_X_DISPLAY_INFO (f)->n_fonts--; +} + +struct xftdraw_list +{ + XftDraw *xftdraw; + struct xftdraw_list *next; +}; + +static struct xftdraw_list *xftdraw_list; + +static void +register_xftdraw (xftdraw) + XftDraw *xftdraw; +{ + struct xftdraw_list *list = malloc (sizeof (struct xftdraw_list)); + + list->xftdraw = xftdraw; + list->next = xftdraw_list; + xftdraw_list = list; +} + +static void +check_xftdraw (xftdraw) + XftDraw *xftdraw; +{ + struct xftdraw_list *list, *prev; + + for (list = xftdraw_list, prev = NULL; list; prev = list, list = list->next) + { + if (list->xftdraw == xftdraw) + { + if (! prev) + { + list = xftdraw_list->next; + free (xftdraw_list); + xftdraw_list = list; + } + else + { + prev->next = list->next; + free (list); + list = prev; + } + return; + } + } + abort (); +} + +static int +xftfont_prepare_face (f, face) + FRAME_PTR f; + struct face *face; +{ + struct xftface_info *xftface_info = malloc (sizeof (struct xftface_info)); + + if (! xftface_info) + return -1; + + BLOCK_INPUT; + xftface_info->xft_draw = XftDrawCreate (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + FRAME_X_VISUAL (f), + FRAME_X_COLORMAP (f)); + register_xftdraw (xftface_info->xft_draw); + + xftfont_get_colors (f, face, face->gc, NULL, + &xftface_info->xft_fg, &xftface_info->xft_bg); + UNBLOCK_INPUT; + + face->extra = xftface_info; + return 0; +} + +static void +xftfont_done_face (f, face) + FRAME_PTR f; + struct face *face; +{ + struct xftface_info *xftface_info = (struct xftface_info *) face->extra; + + if (xftface_info) + { + BLOCK_INPUT; + check_xftdraw (xftface_info->xft_draw); + XftDrawDestroy (xftface_info->xft_draw); + UNBLOCK_INPUT; + free (xftface_info); + face->extra = NULL; + } +} + +static unsigned +xftfont_encode_char (font, c) + struct font *font; + int c; +{ + struct xftfont_info *xftfont_info = (struct xftfont_info *) font; + unsigned code = XftCharIndex (xftfont_info->display, xftfont_info->xftfont, + (FcChar32) c); + + return (code ? code : 0xFFFFFFFF); +} + +static int +xftfont_text_extents (font, code, nglyphs, metrics) + struct font *font; + unsigned *code; + int nglyphs; + struct font_metrics *metrics; +{ + struct xftfont_info *xftfont_info = (struct xftfont_info *) font; + XGlyphInfo extents; + + BLOCK_INPUT; + XftGlyphExtents (xftfont_info->display, xftfont_info->xftfont, code, nglyphs, + &extents); + UNBLOCK_INPUT; + if (metrics) + { + metrics->lbearing = - extents.x; + metrics->rbearing = - extents.x + extents.width; + metrics->width = extents.xOff; + metrics->ascent = extents.y; + metrics->descent = extents.y - extents.height; + } + return extents.xOff; +} + +static int +xftfont_draw (s, from, to, x, y, with_background) + struct glyph_string *s; + int from, to, x, y, with_background; +{ + FRAME_PTR f = s->f; + struct face *face = s->face; + struct xftfont_info *xftfont_info = (struct xftfont_info *) face->font_info; + struct xftface_info *xftface_info = (struct xftface_info *) face->extra; + FT_UInt *code; + XftColor fg, bg; + XRectangle r; + int len = to - from; + int i; + + xftfont_get_colors (f, face, s->gc, xftface_info, + &fg, s->width ? &bg : NULL); + BLOCK_INPUT; + if (s->clip_width) + { + r.x = s->clip_x, r.width = s->clip_width; + r.y = s->clip_y, r.height = s->clip_height; + XftDrawSetClipRectangles (xftface_info->xft_draw, 0, 0, &r, 1); + } + if (with_background) + { + struct font *font = (struct font *) face->font_info; + + XftDrawRect (xftface_info->xft_draw, &bg, + x, y - face->font->ascent, s->width, font->font.height); + } + code = alloca (sizeof (FT_UInt) * len); + for (i = 0; i < len; i++) + code[i] = ((XCHAR2B_BYTE1 (s->char2b + from + i) << 8) + | XCHAR2B_BYTE2 (s->char2b + from + i)); + + XftDrawGlyphs (xftface_info->xft_draw, &fg, xftfont_info->xftfont, + x, y, code, len); + if (s->clip_width) + XftDrawSetClip (xftface_info->xft_draw, NULL); + UNBLOCK_INPUT; + + return len; +} + +static int +xftfont_anchor_point (font, code, index, x, y) + struct font *font; + unsigned code; + int index; + int *x, *y; +{ + struct xftfont_info *xftfont_info = (struct xftfont_info *) font; + FT_Face ft_face = xftfont_info->ft_face; + + if (FT_Load_Glyph (ft_face, code, FT_LOAD_DEFAULT) != 0) + return -1; + if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return -1; + if (index >= ft_face->glyph->outline.n_points) + return -1; + *x = ft_face->glyph->outline.points[index].x; + *y = ft_face->glyph->outline.points[index].y; + return 0; +} + + +void +syms_of_xftfont () +{ + DEFSYM (Qxft, "xft"); + + xftfont_driver = ftfont_driver; + xftfont_driver.type = Qxft; + xftfont_driver.get_cache = xfont_driver.get_cache; + xftfont_driver.list = xftfont_list; + xftfont_driver.open = xftfont_open; + xftfont_driver.close = xftfont_close; + xftfont_driver.prepare_face = xftfont_prepare_face; + xftfont_driver.done_face = xftfont_done_face; + xftfont_driver.encode_char = xftfont_encode_char; + xftfont_driver.text_extents = xftfont_text_extents; + xftfont_driver.draw = xftfont_draw; + xftfont_driver.anchor_point = xftfont_anchor_point; + + register_font_driver (&xftfont_driver, NULL); +} -- 2.20.1