X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/2f4c3d6df6d79fd49cb49cf3aa7f4610b78b21b4..5df4f04cd32af723742c81095b38ae83b3c2b462:/src/ftfont.c diff --git a/src/ftfont.c b/src/ftfont.c index 486fd93be6..e90a2fc565 100644 --- a/src/ftfont.c +++ b/src/ftfont.c @@ -1,6 +1,6 @@ /* ftfont.c -- FreeType font driver. - Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. - Copyright (C) 2006, 2007, 2008 + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 National Institute of Advanced Industrial Science and Technology (AIST) Registration Number H13PRO009 @@ -21,6 +21,7 @@ along with GNU Emacs. If not, see . */ #include #include +#include #include #include @@ -32,6 +33,7 @@ along with GNU Emacs. If not, see . */ #include "character.h" #include "charset.h" #include "coding.h" +#include "composite.h" #include "fontset.h" #include "font.h" #include "ftfont.h" @@ -51,7 +53,7 @@ static FT_Library ft_library; /* Cache for FreeType fonts. */ static Lisp_Object freetype_font_cache; -/* Cache for FT_Face */ +/* Cache for FT_Face and FcCharSet. */ static Lisp_Object ft_face_cache; /* The actual structure for FreeType font that can be casted to struct @@ -60,92 +62,166 @@ static Lisp_Object ft_face_cache; struct ftfont_info { struct font font; - FT_Size ft_size; - int fc_charset_idx; #ifdef HAVE_LIBOTF + /* The following four members must be here in this order to be + compatible with struct xftfont_info (in xftfont.c). */ int maybe_otf; /* Flag to tell if this may be OTF or not. */ OTF *otf; #endif /* HAVE_LIBOTF */ + FT_Size ft_size; + int index; + FT_Matrix matrix; }; -static Lisp_Object ftfont_pattern_entity P_ ((FcPattern *, Lisp_Object, int)); +enum ftfont_cache_for + { + FTFONT_CACHE_FOR_FACE, + FTFONT_CACHE_FOR_CHARSET, + FTFONT_CACHE_FOR_ENTITY + }; + +static Lisp_Object ftfont_pattern_entity P_ ((FcPattern *, Lisp_Object)); + +static Lisp_Object ftfont_resolve_generic_family P_ ((Lisp_Object, + FcPattern *)); +static Lisp_Object ftfont_lookup_cache P_ ((Lisp_Object, + enum ftfont_cache_for)); + +static void ftfont_filter_properties P_ ((Lisp_Object font, Lisp_Object alist)); -static Lisp_Object ftfont_resolve_generic_family P_ ((Lisp_Object)); -Lisp_Object ftfont_font_format P_ ((FcPattern *)); +Lisp_Object ftfont_font_format P_ ((FcPattern *, Lisp_Object)); #define SYMBOL_FcChar8(SYM) (FcChar8 *) SDATA (SYMBOL_NAME (SYM)) static struct { - /* charset name */ + /* registry name */ char *name; /* characters to distinguish the charset from the others */ int uniquifier[6]; - /* set in syms_of_ftfont */ - int charset_id; + /* additional constraint by language */ + char *lang; /* set on demand */ FcCharSet *fc_charset; } fc_charset_table[] = - { { "iso-8859-1", { 0x00A0, 0x00A1, 0x00B4, 0x00BC, 0x00D0 }, -1 }, - { "iso-8859-2", { 0x00A0, 0x010E }}, - { "iso-8859-3", { 0x00A0, 0x0108 }}, - { "iso-8859-4", { 0x00A0, 0x00AF, 0x0128, 0x0156, 0x02C7 }}, - { "iso-8859-5", { 0x00A0, 0x0401 }}, - { "iso-8859-6", { 0x00A0, 0x060C }}, - { "iso-8859-7", { 0x00A0, 0x0384 }}, - { "iso-8859-8", { 0x00A0, 0x05D0 }}, - { "iso-8859-9", { 0x00A0, 0x00A1, 0x00BC, 0x011E }}, - { "iso-8859-10", { 0x00A0, 0x00D0, 0x0128, 0x2015 }}, - { "iso-8859-11", { 0x00A0, 0x0E01 }}, - { "iso-8859-13", { 0x00A0, 0x201C }}, - { "iso-8859-14", { 0x00A0, 0x0174 }}, - { "iso-8859-15", { 0x00A0, 0x00A1, 0x00D0, 0x0152 }}, - { "iso-8859-16", { 0x00A0, 0x0218}}, - { "chinese-gb2312", { 0x4E13 }}, - { "big5", { 0xF6B1 }}, - { "japanese-jisx0208", { 0x4E55 }}, - { "korean-ksc5601", { 0xAC00 }}, - { "chinese-cns11643-1", { 0xFE32 }}, - { "chinese-cns11643-2", { 0x4E33, 0x7934 }}, - { "chinese-cns11643-3", { 0x201A9 }}, - { "chinese-cns11643-4", { 0x20057 }}, - { "chinese-cns11643-5", { 0x20000 }}, - { "chinese-cns11643-6", { 0x20003 }}, - { "chinese-cns11643-7", { 0x20055 }}, - { "chinese-gbk", { 0x4E06 }}, - { "japanese-jisx0212", { 0x4E44 }}, - { "japanese-jisx0213-1", { 0xFA10 }}, - { "japanese-jisx0213-2", { 0xFA49 }}, - { "japanese-jisx0213.2004-1", { 0x20B9F }}, - { "viscii", { 0x1EA0, 0x1EAE, 0x1ED2 }}, - { "tis620", { 0x0E01 }}, - { "windows-1251", { 0x0401, 0x0490 }}, - { "koi8-r", { 0x0401, 0x2219 }}, - { "mule-lao", { 0x0E81 }}, + { { "iso8859-1", { 0x00A0, 0x00A1, 0x00B4, 0x00BC, 0x00D0 } }, + { "iso8859-2", { 0x00A0, 0x010E }}, + { "iso8859-3", { 0x00A0, 0x0108 }}, + { "iso8859-4", { 0x00A0, 0x00AF, 0x0128, 0x0156, 0x02C7 }}, + { "iso8859-5", { 0x00A0, 0x0401 }}, + { "iso8859-6", { 0x00A0, 0x060C }}, + { "iso8859-7", { 0x00A0, 0x0384 }}, + { "iso8859-8", { 0x00A0, 0x05D0 }}, + { "iso8859-9", { 0x00A0, 0x00A1, 0x00BC, 0x011E }}, + { "iso8859-10", { 0x00A0, 0x00D0, 0x0128, 0x2015 }}, + { "iso8859-11", { 0x00A0, 0x0E01 }}, + { "iso8859-13", { 0x00A0, 0x201C }}, + { "iso8859-14", { 0x00A0, 0x0174 }}, + { "iso8859-15", { 0x00A0, 0x00A1, 0x00D0, 0x0152 }}, + { "iso8859-16", { 0x00A0, 0x0218}}, + { "gb2312.1980-0", { 0x4E13 }, "zh-cn"}, + { "big5-0", { 0xF6B1 }, "zh-tw" }, + { "jisx0208.1983-0", { 0x4E55 }, "ja"}, + { "ksc5601.1985-0", { 0xAC00 }, "ko"}, + { "cns11643.1992-1", { 0xFE32 }, "zh-tw"}, + { "cns11643.1992-2", { 0x4E33, 0x7934 }}, + { "cns11643.1992-3", { 0x201A9 }}, + { "cns11643.1992-4", { 0x20057 }}, + { "cns11643.1992-5", { 0x20000 }}, + { "cns11643.1992-6", { 0x20003 }}, + { "cns11643.1992-7", { 0x20055 }}, + { "gbk-0", { 0x4E06 }, "zh-cn"}, + { "jisx0212.1990-0", { 0x4E44 }}, + { "jisx0213.2000-1", { 0xFA10 }, "ja"}, + { "jisx0213.2000-2", { 0xFA49 }}, + { "jisx0213.2004-1", { 0x20B9F }}, + { "viscii1.1-1", { 0x1EA0, 0x1EAE, 0x1ED2 }, "vi"}, + { "tis620.2529-1", { 0x0E01 }, "th"}, + { "windows-1251", { 0x0401, 0x0490 }, "ru"}, + { "koi8-r", { 0x0401, 0x2219 }, "ru"}, + { "mulelao-1", { 0x0E81 }, "lo"}, + { "unicode-sip", { 0x20000 }}, { NULL } }; extern Lisp_Object Qc, Qm, Qp, Qd; +/* Dirty hack for handing ADSTYLE property. + + Fontconfig (actually the underlying FreeType) gives such ADSTYLE + font property of PCF/BDF fonts in FC_STYLE. And, "Bold", + "Oblique", "Italic", or any non-normal SWIDTH property names + (e.g. SemiCondensed) are appended. In addition, if there's no + ADSTYLE property nor non-normal WEIGHT/SLANT/SWIDTH properties, + "Regular" is used for FC_STYLE (see the function + pcf_interpret_style in src/pcf/pcfread.c of FreeType). + + Unfortunately this behavior is not documented, so the following + code may fail if FreeType changes the behavior in the future. */ + +static Lisp_Object +get_adstyle_property (FcPattern *p) +{ + char *str, *end; + Lisp_Object adstyle; + + if (FcPatternGetString (p, FC_STYLE, 0, (FcChar8 **) &str) != FcResultMatch) + return Qnil; + for (end = str; *end && *end != ' '; end++); + if (*end) + { + char *p = alloca (end - str + 1); + memcpy (p, str, end - str); + p[end - str] = '\0'; + end = p + (end - str); + str = p; + } + if (xstrcasecmp (str, "Regular") == 0 + || xstrcasecmp (str, "Bold") == 0 + || xstrcasecmp (str, "Oblique") == 0 + || xstrcasecmp (str, "Italic") == 0) + return Qnil; + adstyle = font_intern_prop (str, end - str, 1); + if (font_style_to_value (FONT_WIDTH_INDEX, adstyle, 0) >= 0) + return Qnil; + return adstyle; +} + static Lisp_Object -ftfont_pattern_entity (p, registry, fc_charset_idx) +ftfont_pattern_entity (p, extra) FcPattern *p; - Lisp_Object registry; - int fc_charset_idx; + Lisp_Object extra; { - Lisp_Object entity; + Lisp_Object key, cache, entity; char *file, *str; + int index; int numeric; double dbl; FcBool b; if (FcPatternGetString (p, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch) return Qnil; + if (FcPatternGetInteger (p, FC_INDEX, 0, &index) != FcResultMatch) + return Qnil; + + key = Fcons (make_unibyte_string ((char *) file, strlen ((char *) file)), + make_number (index)); + cache = ftfont_lookup_cache (key, FTFONT_CACHE_FOR_ENTITY); + entity = XCAR (cache); + if (! NILP (entity)) + { + Lisp_Object val = font_make_entity (); + int i; + for (i = 0; i < FONT_OBJLIST_INDEX; i++) + ASET (val, i, AREF (entity, i)); + return val; + } entity = font_make_entity (); + XSETCAR (cache, entity); ASET (entity, FONT_TYPE_INDEX, Qfreetype); - ASET (entity, FONT_REGISTRY_INDEX, registry); + ASET (entity, FONT_REGISTRY_INDEX, Qiso10646_1); if (FcPatternGetString (p, FC_FOUNDRY, 0, (FcChar8 **) &str) == FcResultMatch) ASET (entity, FONT_FOUNDRY_INDEX, font_intern_prop (str, strlen (str), 1)); @@ -167,7 +243,9 @@ ftfont_pattern_entity (p, registry, fc_charset_idx) FONT_SET_STYLE (entity, FONT_WIDTH_INDEX, make_number (numeric)); } if (FcPatternGetDouble (p, FC_PIXEL_SIZE, 0, &dbl) == FcResultMatch) - ASET (entity, FONT_SIZE_INDEX, make_number (dbl)); + { + 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) @@ -179,12 +257,31 @@ ftfont_pattern_entity (p, registry, fc_charset_idx) } if (FcPatternGetBool (p, FC_SCALABLE, 0, &b) == FcResultMatch && b == FcTrue) - ASET (entity, FONT_AVGWIDTH_INDEX, make_number (0)); + { + ASET (entity, FONT_SIZE_INDEX, make_number (0)); + ASET (entity, FONT_AVGWIDTH_INDEX, make_number (0)); + } + else + { + /* As this font is not scalable, parhaps this is a BDF or PCF + font. */ + FT_Face ft_face; + + ASET (entity, FONT_ADSTYLE_INDEX, get_adstyle_property (p)); + if ((ft_library || FT_Init_FreeType (&ft_library) == 0) + && FT_New_Face (ft_library, file, index, &ft_face) == 0) + { + BDF_PropertyRec rec; + + if (FT_Get_BDF_Property (ft_face, "AVERAGE_WIDTH", &rec) == 0 + && rec.type == BDF_PROPERTY_TYPE_INTEGER) + ASET (entity, FONT_AVGWIDTH_INDEX, make_number (rec.u.integer)); + FT_Done_Face (ft_face); + } + } - font_put_extra (entity, QCfont_entity, - Fcons (make_unibyte_string ((char *) file, - strlen ((char *) file)), - make_number (fc_charset_idx))); + ASET (entity, FONT_EXTRA_INDEX, Fcopy_sequence (extra)); + font_put_extra (entity, QCfont_entity, key); return entity; } @@ -192,12 +289,14 @@ ftfont_pattern_entity (p, registry, fc_charset_idx) static Lisp_Object ftfont_generic_family_list; static Lisp_Object -ftfont_resolve_generic_family (family) +ftfont_resolve_generic_family (family, pattern) Lisp_Object family; + FcPattern *pattern; { Lisp_Object slot; - FcPattern *pattern = NULL, *match; + FcPattern *match; FcResult result; + FcLangSet *langset; family = Fintern (Fdowncase (SYMBOL_NAME (family)), Qnil); if (EQ (family, Qmono)) @@ -209,10 +308,20 @@ ftfont_resolve_generic_family (family) return Qnil; if (! EQ (XCDR (slot), Qt)) return XCDR (slot); - pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, - SYMBOL_FcChar8 (family), (char *) 0); + pattern = FcPatternDuplicate (pattern); if (! pattern) goto err; + FcPatternDel (pattern, FC_FOUNDRY); + FcPatternDel (pattern, FC_FAMILY); + FcPatternAddString (pattern, FC_FAMILY, SYMBOL_FcChar8 (family)); + if (FcPatternGetLangSet (pattern, FC_LANG, 0, &langset) != FcResultMatch) + { + /* This is to avoid the effect of locale. */ + langset = FcLangSetCreate (); + FcLangSetAdd (langset, "en"); + FcPatternAddLangSet (pattern, FC_LANG, langset); + FcLangSetDestroy (langset); + } FcConfigSubstitute (NULL, pattern, FcMatchPattern); FcDefaultSubstitute (pattern); match = FcFontMatch (NULL, pattern, &result); @@ -226,41 +335,157 @@ ftfont_resolve_generic_family (family) else family = Qnil; XSETCDR (slot, family); - err: if (match) FcPatternDestroy (match); + err: if (pattern) FcPatternDestroy (pattern); return family; } -Lisp_Object -ftfont_lookup_cache (filename) - Lisp_Object filename; +struct ftfont_cache_data +{ + FT_Face ft_face; + FcCharSet *fc_charset; +}; + +static Lisp_Object +ftfont_lookup_cache (key, cache_for) + Lisp_Object key; + enum ftfont_cache_for cache_for; { - Lisp_Object cache, val; + Lisp_Object cache, val, entity; + struct ftfont_cache_data *cache_data; + + if (FONT_ENTITY_P (key)) + { + entity = key; + val = assq_no_quit (QCfont_entity, AREF (entity, FONT_EXTRA_INDEX)); + xassert (CONSP (val)); + key = XCDR (val); + } + else + entity = Qnil; - cache = assoc_no_quit (filename, ft_face_cache); + if (NILP (ft_face_cache)) + cache = Qnil; + else + cache = Fgethash (key, ft_face_cache, Qnil); if (NILP (cache)) { + if (NILP (ft_face_cache)) + { + Lisp_Object args[2]; + + args[0] = QCtest; + args[1] = Qequal; + ft_face_cache = Fmake_hash_table (2, args); + } + cache_data = xmalloc (sizeof (struct ftfont_cache_data)); + cache_data->ft_face = NULL; + cache_data->fc_charset = NULL; val = make_save_value (NULL, 0); - cache = Fcons (filename, val); - ft_face_cache = Fcons (cache, ft_face_cache); + XSAVE_VALUE (val)->integer = 0; + XSAVE_VALUE (val)->pointer = cache_data; + cache = Fcons (Qnil, val); + Fputhash (key, cache, ft_face_cache); } else - val = XCDR (cache); - if (! XSAVE_VALUE (val)->pointer) { - FT_Face ft_face; + val = XCDR (cache); + cache_data = XSAVE_VALUE (val)->pointer; + } - if (! ft_library - && FT_Init_FreeType (&ft_library) != 0) - return Qnil; - if (FT_New_Face (ft_library, (char *) SDATA (filename), 0, &ft_face) != 0) - return Qnil; - XSAVE_VALUE (val)->pointer = ft_face; + if (cache_for == FTFONT_CACHE_FOR_ENTITY) + return cache; + + if (cache_for == FTFONT_CACHE_FOR_FACE + ? ! cache_data->ft_face : ! cache_data->fc_charset) + { + char *filename = (char *) SDATA (XCAR (key)); + int index = XINT (XCDR (key)); + + if (cache_for == FTFONT_CACHE_FOR_FACE) + { + if (! ft_library + && FT_Init_FreeType (&ft_library) != 0) + return Qnil; + if (FT_New_Face (ft_library, filename, index, &cache_data->ft_face) + != 0) + return Qnil; + } + else + { + FcPattern *pat = NULL; + FcFontSet *fontset = NULL; + FcObjectSet *objset = NULL; + FcCharSet *charset = NULL; + + pat = FcPatternBuild (0, FC_FILE, FcTypeString, (FcChar8 *) filename, + FC_INDEX, FcTypeInteger, index, NULL); + if (! pat) + goto finish; + objset = FcObjectSetBuild (FC_CHARSET, FC_STYLE, NULL); + if (! objset) + goto finish; + fontset = FcFontList (NULL, pat, objset); + if (! fontset) + goto finish; + if (fontset && fontset->nfont > 0 + && (FcPatternGetCharSet (fontset->fonts[0], FC_CHARSET, 0, + &charset) + == FcResultMatch)) + cache_data->fc_charset = FcCharSetCopy (charset); + else + cache_data->fc_charset = FcCharSetCreate (); + + finish: + if (fontset) + FcFontSetDestroy (fontset); + if (objset) + FcObjectSetDestroy (objset); + if (pat) + FcPatternDestroy (pat); + } } return cache; } +FcCharSet * +ftfont_get_fc_charset (entity) + Lisp_Object entity; +{ + Lisp_Object val, cache; + struct ftfont_cache_data *cache_data; + + cache = ftfont_lookup_cache (entity, FTFONT_CACHE_FOR_CHARSET); + val = XCDR (cache); + cache_data = XSAVE_VALUE (val)->pointer; + return cache_data->fc_charset; +} + +#ifdef HAVE_LIBOTF +static OTF * +ftfont_get_otf (ftfont_info) + struct ftfont_info *ftfont_info; +{ + OTF *otf; + + if (ftfont_info->otf) + return ftfont_info->otf; + if (! ftfont_info->maybe_otf) + return NULL; + otf = OTF_open_ft_face (ftfont_info->ft_size->face); + if (! otf || OTF_get_table (otf, "head") < 0) + { + if (otf) + OTF_close (otf); + ftfont_info->maybe_otf = 0; + return NULL; + } + ftfont_info->otf = otf; + return otf; +} +#endif /* HAVE_LIBOTF */ + static Lisp_Object ftfont_get_cache P_ ((FRAME_PTR)); static Lisp_Object ftfont_list P_ ((Lisp_Object, Lisp_Object)); static Lisp_Object ftfont_match P_ ((Lisp_Object, Lisp_Object)); @@ -275,8 +500,14 @@ 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 *)); +static Lisp_Object ftfont_otf_capability P_ ((struct font *)); static Lisp_Object ftfont_shape P_ ((Lisp_Object)); +#ifdef HAVE_OTF_GET_VARIATION_GLYPHS +static int ftfont_variation_glyphs P_ ((struct font *, int c, + unsigned variations[256])); +#endif /* HAVE_OTF_GET_VARIATION_GLYPHS */ + struct font_driver ftfont_driver = { 0, /* Qfreetype */ @@ -285,31 +516,44 @@ struct font_driver ftfont_driver = ftfont_list, ftfont_match, ftfont_list_family, - NULL, + NULL, /* free_entity */ ftfont_open, ftfont_close, /* We can't draw a text without device dependent functions. */ - NULL, - NULL, + NULL, /* prepare_face */ + NULL, /* done_face */ ftfont_has_char, ftfont_encode_char, ftfont_text_extents, /* We can't draw a text without device dependent functions. */ - NULL, + NULL, /* draw */ ftfont_get_bitmap, - NULL, - NULL, - NULL, + NULL, /* get_bitmap */ + NULL, /* free_bitmap */ + NULL, /* get_outline */ ftfont_anchor_point, +#ifdef HAVE_LIBOTF + ftfont_otf_capability, +#else /* not HAVE_LIBOTF */ NULL, - NULL, - NULL, - NULL, +#endif /* not HAVE_LIBOTF */ + NULL, /* otf_drive */ + NULL, /* start_for_frame */ + NULL, /* end_for_frame */ #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF) - ftfont_shape + ftfont_shape, #else /* not (HAVE_M17N_FLT && HAVE_LIBOTF) */ - NULL + NULL, #endif /* not (HAVE_M17N_FLT && HAVE_LIBOTF) */ + NULL, /* check */ + +#ifdef HAVE_OTF_GET_VARIATION_GLYPHS + ftfont_variation_glyphs, +#else + NULL, +#endif + + ftfont_filter_properties, /* filter_properties */ }; extern Lisp_Object QCname; @@ -325,25 +569,25 @@ static int ftfont_get_charset (registry) Lisp_Object registry; { - struct charset *encoding; + char *str = (char *) SDATA (SYMBOL_NAME (registry)); + char *re = alloca (SBYTES (SYMBOL_NAME (registry)) * 2 + 1); + Lisp_Object regexp; int i, j; - if (font_registry_charsets (registry, &encoding, NULL) < 0) - return -1; - if (fc_charset_table[0].charset_id < 0) - /* Setup charset_id field of all elements. */ - for (i = 0; fc_charset_table[i].name; i++) - { - Lisp_Object sym = intern (fc_charset_table[i].name); - - if (CHARSETP (sym)) - fc_charset_table[i].charset_id = XINT (CHARSET_SYMBOL_ID (sym)); - else - fc_charset_table[i].charset_id = -1; - } - + for (i = j = 0; i < SBYTES (SYMBOL_NAME (registry)); i++, j++) + { + if (str[i] == '.') + re[j++] = '\\'; + else if (str[i] == '*') + re[j++] = '.'; + re[j] = str[i]; + if (re[j] == '?') + re[j] = '.'; + } + re[j] = '\0'; + regexp = make_unibyte_string (re, j); for (i = 0; fc_charset_table[i].name; i++) - if (encoding->id == fc_charset_table[i].charset_id) + if (fast_c_string_match_ignore_case (regexp, fc_charset_table[i].name) >= 0) break; if (! fc_charset_table[i].name) return -1; @@ -388,6 +632,15 @@ struct OpenTypeSpec (P)[4] = '\0'; \ } while (0) +#define OTF_TAG_SYM(SYM, TAG) \ + do { \ + char str[5]; \ + \ + OTF_TAG_STR (TAG, str); \ + (SYM) = font_intern_prop (str, 4, 1); \ + } while (0) + + static struct OpenTypeSpec * ftfont_get_open_type_spec (Lisp_Object otf_spec) { @@ -410,19 +663,19 @@ ftfont_get_open_type_spec (Lisp_Object otf_spec) else spec->script_tag = 0x44464C54; /* "DFLT" */ otf_spec = XCDR (otf_spec); - val = XCAR (otf_spec); - if (! NILP (val)) - OTF_SYM_TAG (val, spec->langsys_tag); - else - spec->langsys_tag = 0; + spec->langsys_tag = 0; + if (! NILP (otf_spec)) + { + val = XCAR (otf_spec); + if (! NILP (val)) + OTF_SYM_TAG (val, spec->langsys_tag); + otf_spec = XCDR (otf_spec); + } spec->nfeatures[0] = spec->nfeatures[1] = 0; - for (i = 0; i < 2; i++) + for (i = 0; i < 2 && ! NILP (otf_spec); i++, otf_spec = XCDR (otf_spec)) { Lisp_Object len; - otf_spec = XCDR (otf_spec); - if (NILP (otf_spec)) - break; val = XCAR (otf_spec); if (NILP (val)) continue; @@ -452,15 +705,16 @@ ftfont_get_open_type_spec (Lisp_Object otf_spec) return spec; } -static FcPattern *ftfont_spec_pattern P_ ((Lisp_Object, int *, char *, - struct OpenTypeSpec **)); +static FcPattern *ftfont_spec_pattern P_ ((Lisp_Object, char *, + struct OpenTypeSpec **, + char **langname)); static FcPattern * -ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) +ftfont_spec_pattern (spec, otlayout, otspec, langname) Lisp_Object spec; - int *fc_charset_idx; char *otlayout; struct OpenTypeSpec **otspec; + char **langname; { Lisp_Object tmp, extra; FcPattern *pattern = NULL; @@ -469,14 +723,10 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) int n; int dpi = -1; int scalable = -1; - Lisp_Object name = Qnil; Lisp_Object script = Qnil; Lisp_Object registry; + int fc_charset_idx; - if (! NILP (AREF (spec, FONT_ADSTYLE_INDEX)) - && SBYTES (SYMBOL_NAME (AREF (spec, FONT_ADSTYLE_INDEX))) > 0) - /* Fontconfig doesn't support adstyle property. */ - return NULL; if ((n = FONT_SLANT_NUMERIC (spec)) >= 0 && n < 100) /* Fontconfig doesn't support reverse-italic/obligue. */ @@ -492,15 +742,25 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) if (NILP (registry) || EQ (registry, Qascii_0) || EQ (registry, Qiso10646_1) - || EQ (registry, Qunicode_bmp) - || EQ (registry, Qunicode_sip)) - *fc_charset_idx = -1; + || EQ (registry, Qunicode_bmp)) + fc_charset_idx = -1; else { - *fc_charset_idx = ftfont_get_charset (registry); - if (*fc_charset_idx < 0) + FcChar8 *lang; + + fc_charset_idx = ftfont_get_charset (registry); + if (fc_charset_idx < 0) return NULL; - charset = fc_charset_table[*fc_charset_idx].fc_charset; + charset = fc_charset_table[fc_charset_idx].fc_charset; + *langname = fc_charset_table[fc_charset_idx].lang; + lang = (FcChar8 *) *langname; + if (lang) + { + langset = FcLangSetCreate (); + if (! langset) + goto err; + FcLangSetAdd (langset, lang); + } } otlayout[0] = '\0'; @@ -512,11 +772,10 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) key = XCAR (XCAR (extra)), val = XCDR (XCAR (extra)); if (EQ (key, QCdpi)) dpi = XINT (val); - else if (EQ (key, QCfc_unknown_spec)) - name = val; else if (EQ (key, QClang)) { - langset = FcLangSetCreate (); + if (! langset) + langset = FcLangSetCreate (); if (! langset) goto err; if (SYMBOLP (val)) @@ -530,8 +789,6 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) && ! FcLangSetAdd (langset, SYMBOL_FcChar8 (XCAR (val)))) goto err; } - else if (EQ (key, QCname)) - name = val; else if (EQ (key, QCotf)) { *otspec = ftfont_get_open_type_spec (val); @@ -551,7 +808,7 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) { Lisp_Object chars = assq_no_quit (script, Vscript_representative_chars); - if (CONSP (chars)) + if (CONSP (chars) && CONSP (CDR (chars))) { charset = FcCharSetCreate (); if (! charset) @@ -563,13 +820,9 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) } } - pattern = NILP (name) ? FcPatternCreate () : FcNameParse (SDATA (name)); + pattern = FcPatternCreate (); if (! pattern) goto err; - FcPatternDel (pattern, FC_SIZE); - FcPatternDel (pattern, FC_PIXEL_SIZE); - FcPatternDel (pattern, FC_FOUNDRY); - FcPatternDel (pattern, FC_FAMILY); tmp = AREF (spec, FONT_FOUNDRY_INDEX); if (! NILP (tmp) && ! FcPatternAddString (pattern, FC_FOUNDRY, SYMBOL_FcChar8 (tmp))) @@ -590,6 +843,7 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) if (scalable >= 0 && ! FcPatternAddBool (pattern, FC_SCALABLE, scalable ? FcTrue : FcFalse)) goto err; + goto finish; err: @@ -612,7 +866,7 @@ ftfont_spec_pattern (spec, fc_charset_idx, otlayout, otspec) finish: if (langset) FcLangSetDestroy (langset); - if (charset && *fc_charset_idx < 0) FcCharSetDestroy (charset); + if (charset && fc_charset_idx < 0) FcCharSetDestroy (charset); return pattern; } @@ -620,15 +874,18 @@ static Lisp_Object ftfont_list (frame, spec) Lisp_Object frame, spec; { - Lisp_Object val = Qnil, registry, family; + Lisp_Object val = Qnil, family, adstyle; int i; FcPattern *pattern; FcFontSet *fontset = NULL; FcObjectSet *objset = NULL; - int fc_charset_idx; + FcCharSet *charset; + Lisp_Object chars = Qnil; + FcResult result; char otlayout[15]; /* For "otlayout:XXXX" */ struct OpenTypeSpec *otspec = NULL; int spacing = -1; + char *langname = NULL; if (! fc_initialized) { @@ -636,18 +893,28 @@ ftfont_list (frame, spec) fc_initialized = 1; } - pattern = ftfont_spec_pattern (spec, &fc_charset_idx, otlayout, &otspec); + pattern = ftfont_spec_pattern (spec, otlayout, &otspec, &langname); if (! pattern) return Qnil; + if (FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset) != FcResultMatch) + { + val = assq_no_quit (QCscript, AREF (spec, FONT_EXTRA_INDEX)); + if (! NILP (val)) + { + val = assq_no_quit (XCDR (val), Vscript_representative_chars); + if (CONSP (val) && VECTORP (XCDR (val))) + chars = XCDR (val); + } + val = Qnil; + } if (INTEGERP (AREF (spec, FONT_SPACING_INDEX))) spacing = XINT (AREF (spec, FONT_SPACING_INDEX)); - registry = AREF (spec, FONT_REGISTRY_INDEX); family = AREF (spec, FONT_FAMILY_INDEX); if (! NILP (family)) { Lisp_Object resolved; - resolved = ftfont_resolve_generic_family (family); + resolved = ftfont_resolve_generic_family (family, pattern); if (! NILP (resolved)) { FcPatternDel (pattern, FC_FAMILY); @@ -656,20 +923,27 @@ ftfont_list (frame, spec) goto err; } } - + adstyle = AREF (spec, FONT_ADSTYLE_INDEX); + if (! NILP (adstyle) && SBYTES (SYMBOL_NAME (adstyle)) == 0) + adstyle = Qnil; objset = FcObjectSetBuild (FC_FOUNDRY, FC_FAMILY, FC_WEIGHT, FC_SLANT, FC_WIDTH, FC_PIXEL_SIZE, FC_SPACING, FC_SCALABLE, - FC_FILE, + FC_STYLE, FC_FILE, FC_INDEX, #ifdef FC_CAPABILITY FC_CAPABILITY, #endif /* FC_CAPABILITY */ +#ifdef FC_FONTFORMAT + FC_FONTFORMAT, +#endif NULL); if (! objset) goto err; + if (! NILP (chars)) + FcObjectSetAdd (objset, FC_CHARSET); fontset = FcFontList (NULL, pattern, objset); - if (! fontset) - goto err; + if (! fontset || fontset->nfont == 0) + goto finish; #if 0 /* Need fix because this finds any fonts. */ if (fontset->nfont == 0 && ! NILP (family)) @@ -691,7 +965,7 @@ ftfont_list (frame, spec) FcPatternAddString (pattern, FC_FAMILY, fam); FcFontSetDestroy (fontset); fontset = FcFontList (NULL, pattern, objset); - if (fontset->nfont > 0) + if (fontset && fontset->nfont > 0) break; } } @@ -716,8 +990,8 @@ ftfont_list (frame, spec) { FcChar8 *this; - if (FcPatternGetString (fontset->fonts[i], FC_CAPABILITY, 0, - &this) != FcResultMatch + if (FcPatternGetString (fontset->fonts[i], FC_CAPABILITY, 0, &this) + != FcResultMatch || ! strstr ((char *) this, otlayout)) continue; } @@ -745,12 +1019,40 @@ ftfont_list (frame, spec) continue; } #endif /* HAVE_LIBOTF */ - entity = ftfont_pattern_entity (fontset->fonts[i], registry, - fc_charset_idx); + if (VECTORP (chars)) + { + int j; + + if (FcPatternGetCharSet (fontset->fonts[i], FC_CHARSET, 0, &charset) + != FcResultMatch) + continue; + for (j = 0; j < ASIZE (chars); j++) + if (NATNUMP (AREF (chars, j)) + && FcCharSetHasChar (charset, XFASTINT (AREF (chars, j)))) + break; + if (j == ASIZE (chars)) + continue; + } + if (! NILP (adstyle) || langname) + { + Lisp_Object this_adstyle = get_adstyle_property (fontset->fonts[i]); + + if (! NILP (adstyle) + && (NILP (this_adstyle) + || xstrcasecmp (SDATA (SYMBOL_NAME (adstyle)), + SDATA (SYMBOL_NAME (this_adstyle))) != 0)) + continue; + if (langname + && ! NILP (this_adstyle) + && xstrcasecmp (langname, SDATA (SYMBOL_NAME (this_adstyle)))) + continue; + } + entity = ftfont_pattern_entity (fontset->fonts[i], + AREF (spec, FONT_EXTRA_INDEX)); if (! NILP (entity)) val = Fcons (entity, val); } - font_add_log ("ftfont-list", spec, val); + val = Fnreverse (val); goto finish; err: @@ -759,6 +1061,7 @@ ftfont_list (frame, spec) val = Qnil; finish: + FONT_ADD_LOG ("ftfont-list", spec, val); if (objset) FcObjectSetDestroy (objset); if (fontset) FcFontSetDestroy (fontset); if (pattern) FcPatternDestroy (pattern); @@ -769,12 +1072,12 @@ static Lisp_Object ftfont_match (frame, spec) Lisp_Object frame, spec; { - Lisp_Object entity; + Lisp_Object entity = Qnil; FcPattern *pattern, *match = NULL; FcResult result; char otlayout[15]; /* For "otlayout:XXXX" */ struct OpenTypeSpec *otspec = NULL; - int fc_charset_idx; + char *langname = NULL; if (! fc_initialized) { @@ -782,7 +1085,7 @@ ftfont_match (frame, spec) fc_initialized = 1; } - pattern = ftfont_spec_pattern (spec, &fc_charset_idx, otlayout, &otspec); + pattern = ftfont_spec_pattern (spec, otlayout, &otspec, &langname); if (! pattern) return Qnil; @@ -800,7 +1103,7 @@ ftfont_match (frame, spec) match = FcFontMatch (NULL, pattern, &result); if (match) { - entity = ftfont_pattern_entity (match, Qunicode_bmp, fc_charset_idx); + entity = ftfont_pattern_entity (match, AREF (spec, FONT_EXTRA_INDEX)); FcPatternDestroy (match); if (! NILP (AREF (spec, FONT_FAMILY_INDEX)) && NILP (assq_no_quit (AREF (spec, FONT_FAMILY_INDEX), @@ -812,7 +1115,7 @@ ftfont_match (frame, spec) } FcPatternDestroy (pattern); - font_add_log ("ftfont-match", spec, entity); + FONT_ADD_LOG ("ftfont-match", spec, entity); return entity; } @@ -820,7 +1123,7 @@ static Lisp_Object ftfont_list_family (frame) Lisp_Object frame; { - Lisp_Object list; + Lisp_Object list = Qnil; FcPattern *pattern = NULL; FcFontSet *fontset = NULL; FcObjectSet *objset = NULL; @@ -842,7 +1145,6 @@ ftfont_list_family (frame) if (! fontset) goto finish; - list = Qnil; for (i = 0; i < fontset->nfont; i++) { FcPattern *pat = fontset->fonts[i]; @@ -869,12 +1171,11 @@ ftfont_open (f, entity, pixel_size) { struct ftfont_info *ftfont_info; struct font *font; + struct ftfont_cache_data *cache_data; FT_Face ft_face; FT_Size ft_size; FT_UInt size; - Lisp_Object val, filename, cache, font_object; - int fc_charset_idx; - FcPattern *pattern; + Lisp_Object val, filename, index, cache, font_object; int scalable; int spacing; char name[256]; @@ -885,14 +1186,14 @@ ftfont_open (f, entity, pixel_size) if (! CONSP (val)) return Qnil; val = XCDR (val); - filename = XCAR (val); - fc_charset_idx = XINT (XCDR (val)); - cache = ftfont_lookup_cache (filename); + cache = ftfont_lookup_cache (entity, FTFONT_CACHE_FOR_FACE); if (NILP (cache)) return Qnil; - filename = XCAR (cache); + filename = XCAR (val); + index = XCDR (val); val = XCDR (cache); - ft_face = XSAVE_VALUE (val)->pointer; + cache_data = XSAVE_VALUE (XCDR (cache))->pointer; + ft_face = cache_data->ft_face; if (XSAVE_VALUE (val)->integer > 0) { /* FT_Face in this cache is already used by the different size. */ @@ -915,30 +1216,29 @@ ftfont_open (f, entity, pixel_size) return Qnil; } - font_object = font_make_object (VECSIZE (struct ftfont_info)); + font_object = font_make_object (VECSIZE (struct ftfont_info), entity, size); ASET (font_object, FONT_TYPE_INDEX, Qfreetype); - for (i = 1;i < FONT_ENTITY_MAX; i++) - ASET (font_object, i, AREF (entity, i)); - ASET (font_object, FONT_SIZE_INDEX, make_number (size)); len = font_unparse_xlfd (entity, size, name, 256); if (len > 0) - ASET (font_object, FONT_NAME_INDEX, make_unibyte_string (name, len)); + ASET (font_object, FONT_NAME_INDEX, make_string (name, len)); len = font_unparse_fcname (entity, size, name, 256); if (len > 0) - ASET (font_object, FONT_FULLNAME_INDEX, make_unibyte_string (name, len)); + ASET (font_object, FONT_FULLNAME_INDEX, make_string (name, len)); else ASET (font_object, FONT_FULLNAME_INDEX, AREF (font_object, FONT_NAME_INDEX)); ASET (font_object, FONT_FILE_INDEX, filename); - ASET (font_object, FONT_FORMAT_INDEX, ftfont_font_format (pattern)); + ASET (font_object, FONT_FORMAT_INDEX, ftfont_font_format (NULL, filename)); font = XFONT_OBJECT (font_object); ftfont_info = (struct ftfont_info *) font; ftfont_info->ft_size = ft_face->size; - ftfont_info->fc_charset_idx = fc_charset_idx; + ftfont_info->index = XINT (index); #ifdef HAVE_LIBOTF ftfont_info->maybe_otf = ft_face->face_flags & FT_FACE_FLAG_SFNT; ftfont_info->otf = NULL; #endif /* HAVE_LIBOTF */ + /* This means that there's no need of transformation. */ + ftfont_info->matrix.xx = 0; font->pixel_size = size; font->driver = &ftfont_driver; font->encoding_charset = font->repertory_charset = -1; @@ -962,7 +1262,7 @@ ftfont_open (f, entity, pixel_size) spacing = XINT (AREF (entity, FONT_SPACING_INDEX)); else spacing = FC_PROPORTIONAL; - if (spacing != FC_PROPORTIONAL) + if (spacing != FC_PROPORTIONAL && spacing != FC_DUAL) font->min_width = font->average_width = font->space_width = (scalable ? ft_face->max_advance_width * size / upEM : ft_face->size->metrics.max_advance >> 6); @@ -972,7 +1272,7 @@ ftfont_open (f, entity, pixel_size) font->min_width = font->average_width = font->space_width = 0; for (i = 32, n = 0; i < 127; i++) - if (FT_Load_Char (ft_face, i, FT_LOAD_DEFAULT) != 0) + if (FT_Load_Char (ft_face, i, FT_LOAD_DEFAULT) == 0) { int this_width = ft_face->glyph->metrics.horiAdvance >> 6; @@ -995,7 +1295,7 @@ ftfont_open (f, entity, pixel_size) if (scalable) { font->underline_position = -ft_face->underline_position * size / upEM; - font->underline_thickness = -ft_face->underline_thickness * size / upEM; + font->underline_thickness = ft_face->underline_thickness * size / upEM; } else { @@ -1014,39 +1314,56 @@ ftfont_close (f, font) struct ftfont_info *ftfont_info = (struct ftfont_info *) font; Lisp_Object val, cache; - cache = ftfont_lookup_cache (font->props[FONT_FILE_INDEX]); + val = Fcons (font->props[FONT_FILE_INDEX], make_number (ftfont_info->index)); + cache = ftfont_lookup_cache (val, FTFONT_CACHE_FOR_FACE); + xassert (CONSP (cache)); val = XCDR (cache); (XSAVE_VALUE (val)->integer)--; if (XSAVE_VALUE (val)->integer == 0) { - FT_Face ft_face = XSAVE_VALUE (val)->pointer; + struct ftfont_cache_data *cache_data = XSAVE_VALUE (val)->pointer; - FT_Done_Face (ft_face); + FT_Done_Face (cache_data->ft_face); #ifdef HAVE_LIBOTF if (ftfont_info->otf) OTF_close (ftfont_info->otf); #endif - XSAVE_VALUE (val)->pointer = NULL; + cache_data->ft_face = NULL; } else FT_Done_Size (ftfont_info->ft_size); } static int -ftfont_has_char (entity, c) - Lisp_Object entity; +ftfont_has_char (font, c) + Lisp_Object font; int c; { - Lisp_Object val; - int fc_charset_idx; - struct charset *charset; + struct charset *cs = NULL; + + if (EQ (AREF (font, FONT_ADSTYLE_INDEX), Qja) + && charset_jisx0208 >= 0) + cs = CHARSET_FROM_ID (charset_jisx0208); + else if (EQ (AREF (font, FONT_ADSTYLE_INDEX), Qko) + && charset_ksc5601 >= 0) + cs = CHARSET_FROM_ID (charset_ksc5601); + if (cs) + return (ENCODE_CHAR (cs, c) != CHARSET_INVALID_CODE (cs)); + + if (FONT_ENTITY_P (font)) + { + FcCharSet *charset = ftfont_get_fc_charset (font); - val = assq_no_quit (QCfont_entity, AREF (entity, FONT_EXTRA_INDEX)); - fc_charset_idx = XINT (XCDR (XCDR (val))); - if (fc_charset_idx < 0) - return -1; - charset = CHARSET_FROM_ID (fc_charset_table[fc_charset_idx].charset_id); - return (ENCODE_CHAR (charset, c) != CHARSET_INVALID_CODE (charset)); + return (FcCharSetHasChar (charset, c) == FcTrue); + } + else + { + struct ftfont_info *ftfont_info; + + ftfont_info = (struct ftfont_info *) XFONT_OBJECT (font); + return (FT_Get_Char_Index (ftfont_info->ft_size->face, (FT_ULong) c) + != 0); + } } static unsigned @@ -1072,18 +1389,29 @@ ftfont_text_extents (font, code, nglyphs, metrics) struct ftfont_info *ftfont_info = (struct ftfont_info *) font; FT_Face ft_face = ftfont_info->ft_size->face; int width = 0; - int i; + int i, first; 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++) + for (i = 0, first = 1; i < nglyphs; i++) { if (FT_Load_Glyph (ft_face, code[i], FT_LOAD_DEFAULT) == 0) { FT_Glyph_Metrics *m = &ft_face->glyph->metrics; + if (first) + { + if (metrics) + { + metrics->lbearing = m->horiBearingX >> 6; + metrics->rbearing = (m->horiBearingX + m->width) >> 6; + metrics->ascent = m->horiBearingY >> 6; + metrics->descent = (m->height - m->horiBearingY) >> 6; + } + first = 0; + } if (metrics) { if (metrics->lbearing > width + (m->horiBearingX >> 6)) @@ -1094,8 +1422,8 @@ ftfont_text_extents (font, code, nglyphs, metrics) = 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; + if (metrics->descent > ((m->height - m->horiBearingY) >> 6)) + metrics->descent = (m->height - m->horiBearingY) >> 6; } width += m->horiAdvance >> 6; } @@ -1182,14 +1510,89 @@ ftfont_anchor_point (font, code, index, x, y) } #ifdef HAVE_LIBOTF + +static Lisp_Object +ftfont_otf_features (gsub_gpos) + OTF_GSUB_GPOS *gsub_gpos; +{ + Lisp_Object scripts, langsyses, features, sym; + int i, j, k, l; + + for (scripts = Qnil, i = gsub_gpos->ScriptList.ScriptCount - 1; i >= 0; i--) + { + OTF_Script *otf_script = gsub_gpos->ScriptList.Script + i; + + for (langsyses = Qnil, j = otf_script->LangSysCount - 1; j >= -1; j--) + { + OTF_LangSys *otf_langsys; + + if (j >= 0) + otf_langsys = otf_script->LangSys + j; + else if (otf_script->DefaultLangSysOffset) + otf_langsys = &otf_script->DefaultLangSys; + else + break; + + for (features = Qnil, k = otf_langsys->FeatureCount - 1; k >= 0; k--) + { + l = otf_langsys->FeatureIndex[k]; + if (l >= gsub_gpos->FeatureList.FeatureCount) + continue; + OTF_TAG_SYM (sym, gsub_gpos->FeatureList.Feature[l].FeatureTag); + features = Fcons (sym, features); + } + if (j >= 0) + OTF_TAG_SYM (sym, otf_script->LangSysRecord[j].LangSysTag); + else + sym = Qnil; + langsyses = Fcons (Fcons (sym, features), langsyses); + } + + OTF_TAG_SYM (sym, gsub_gpos->ScriptList.Script[i].ScriptTag); + scripts = Fcons (Fcons (sym, langsyses), scripts); + } + return scripts; + +} + + +static Lisp_Object +ftfont_otf_capability (font) + struct font *font; +{ + struct ftfont_info *ftfont_info = (struct ftfont_info *) font; + OTF *otf = ftfont_get_otf (ftfont_info); + Lisp_Object gsub_gpos; + + if (! otf) + return Qnil; + gsub_gpos = Fcons (Qnil, Qnil); + if (OTF_get_table (otf, "GSUB") == 0 + && otf->gsub->FeatureList.FeatureCount > 0) + XSETCAR (gsub_gpos, ftfont_otf_features (otf->gsub)); + if (OTF_get_table (otf, "GPOS") == 0 + && otf->gpos->FeatureList.FeatureCount > 0) + XSETCDR (gsub_gpos, ftfont_otf_features (otf->gpos)); + return gsub_gpos; +} + #ifdef HAVE_M17N_FLT +#if (((LIBOTF_MAJOR_VERSION > 1) || (LIBOTF_RELEASE_NUMBER >= 10)) \ + && ((M17NLIB_MAJOR_VERSION > 1) || (M17NLIB_MINOR_VERSION >= 6))) +/* We can use the new feature of libotf and m17n-flt to handle the + character encoding scheme introduced in Unicode 5.1 and 5.2 for + some Agian scripts. */ +#define M17N_FLT_USE_NEW_FEATURE +#endif + struct MFLTFontFT { MFLTFont flt_font; struct font *font; FT_Face ft_face; OTF *otf; + FT_Matrix *matrix; }; static int @@ -1213,6 +1616,12 @@ ftfont_get_glyph_id (font, gstring, from, to) return 0; } +/* Operators for 26.6 fixed fractional pixel format */ + +#define FLOOR(x) ((x) & -64) +#define CEIL(x) (((x)+63) & -64) +#define ROUND(x) (((x)+32) & -64) + static int ftfont_get_metrics (font, gstring, from, to) MFLTFont *font; @@ -1229,16 +1638,35 @@ ftfont_get_metrics (font, gstring, from, to) if (g->code != FONT_INVALID_CODE) { FT_Glyph_Metrics *m; + int lbearing, rbearing, ascent, descent, xadv; if (FT_Load_Glyph (ft_face, g->code, FT_LOAD_DEFAULT) != 0) abort (); m = &ft_face->glyph->metrics; - - g->lbearing = m->horiBearingX; - g->rbearing = m->horiBearingX + m->width; - g->ascent = m->horiBearingY; - g->descent = m->height - m->horiBearingY; - g->xadv = m->horiAdvance; + if (flt_font_ft->matrix) + { + FT_Vector v[4]; + int i; + + v[0].x = v[1].x = m->horiBearingX; + v[2].x = v[3].x = m->horiBearingX + m->width; + v[0].y = v[2].y = m->horiBearingY; + v[1].y = v[3].y = m->horiBearingY - m->height; + for (i = 0; i < 4; i++) + FT_Vector_Transform (v + i, flt_font_ft->matrix); + g->lbearing = v[0].x < v[1].x ? FLOOR (v[0].x) : FLOOR (v[1].x); + g->rbearing = v[2].x > v[3].x ? CEIL (v[2].x) : CEIL (v[3].x); + g->ascent = v[0].y > v[2].y ? CEIL (v[0].y) : CEIL (v[2].y); + g->descent = v[1].y < v[3].y ? - FLOOR (v[1].y) : - FLOOR (v[3].y); + } + else + { + g->lbearing = FLOOR (m->horiBearingX); + g->rbearing = CEIL (m->horiBearingX + m->width); + g->ascent = CEIL (m->horiBearingY); + g->descent = - FLOOR (m->horiBearingY - m->height); + } + g->xadv = ROUND (ft_face->glyph->advance.x); } else { @@ -1276,10 +1704,16 @@ ftfont_check_otf (MFLTFont *font, MFLTOtfSpec *spec) else tags[n] = spec->features[i][n]; } +#ifdef M17N_FLT_USE_NEW_FEATURE + if (OTF_check_features (otf, i == 0, spec->script, spec->langsys, + tags, n - negative) != 1) + return 0; +#else /* not M17N_FLT_USE_NEW_FEATURE */ if (n - negative > 0 && OTF_check_features (otf, i == 0, spec->script, spec->langsys, tags, n - negative) != 1) return 0; +#endif /* not M17N_FLT_USE_NEW_FEATURE */ } return 1; } @@ -1308,15 +1742,50 @@ adjust_anchor (FT_Face ft_face, OTF_Anchor *anchor, } else if (anchor->AnchorFormat == 3) { - if (anchor->f.f2.XDeviceTable.offset) + if (anchor->f.f2.XDeviceTable.offset + && anchor->f.f2.XDeviceTable.DeltaValue) *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, x_ppem); - if (anchor->f.f2.YDeviceTable.offset) + if (anchor->f.f2.YDeviceTable.offset + && anchor->f.f2.YDeviceTable.DeltaValue) *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, y_ppem); } } static OTF_GlyphString otf_gstring; +static void +setup_otf_gstring (int size) +{ + if (otf_gstring.size == 0) + { + otf_gstring.glyphs = (OTF_Glyph *) xmalloc (sizeof (OTF_Glyph) * size); + otf_gstring.size = size; + } + else if (otf_gstring.size < size) + { + otf_gstring.glyphs = xrealloc (otf_gstring.glyphs, + sizeof (OTF_Glyph) * size); + otf_gstring.size = size; + } + otf_gstring.used = size; + memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * size); +} + +#ifdef M17N_FLT_USE_NEW_FEATURE + +/* Pack 32-bit OTF tag (0x7F7F7F7F) into 28-bit (0x0FFFFFFF). */ +#define PACK_OTF_TAG(TAG) \ + ((((TAG) & 0x7F000000) >> 3) \ + | (((TAG) & 0x7F0000) >> 2) \ + | (((TAG) & 0x7F00) >> 1) \ + | ((TAG) & 0x7F)) + +/* Assuming that FONT is an OpenType font, apply OpenType features + specified in SPEC on glyphs between FROM and TO of IN, and record + the lastly applied feature in each glyph of IN. If OUT is not + NULL, append the resulting glyphs to OUT while storing glyph + position adjustment information in ADJUSTMENT. */ + static int ftfont_drive_otf (font, spec, in, from, to, out, adjustment) MFLTFont *font; @@ -1334,6 +1803,7 @@ ftfont_drive_otf (font, spec, in, from, to, out, adjustment) OTF_Glyph *otfg; char script[5], *langsys = NULL; char *gsub_features = NULL, *gpos_features = NULL; + OTF_Feature *features; if (len == 0) return from; @@ -1369,19 +1839,7 @@ ftfont_drive_otf (font, spec, in, from, to, out, adjustment) } } - if (otf_gstring.size == 0) - { - otf_gstring.glyphs = (OTF_Glyph *) malloc (sizeof (OTF_Glyph) * len); - otf_gstring.size = len; - } - else if (otf_gstring.size < len) - { - otf_gstring.glyphs = (OTF_Glyph *) realloc (otf_gstring.glyphs, - sizeof (OTF_Glyph) * len); - otf_gstring.size = len; - } - otf_gstring.used = len; - memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * len); + setup_otf_gstring (len); for (i = 0; i < len; i++) { otf_gstring.glyphs[i].c = in->glyphs[from + i].c; @@ -1389,20 +1847,22 @@ ftfont_drive_otf (font, spec, in, from, to, out, adjustment) } OTF_drive_gdef (otf, &otf_gstring); - gidx = out->used; + gidx = out ? out->used : from; - if (gsub_features) + if (gsub_features && out) { - if (OTF_drive_gsub (otf, &otf_gstring, script, langsys, gsub_features) - < 0) + if (OTF_drive_gsub_with_log (otf, &otf_gstring, script, langsys, + gsub_features) < 0) goto simple_copy; if (out->allocated < out->used + otf_gstring.used) return -2; + features = otf->gsub->FeatureList.Feature; for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; ) { MFLTGlyph *g; int min_from, max_to; int j; + int feature_idx = otfg->positioning_type >> 4; g = out->glyphs + out->used; *g = in->glyphs[from + otfg->f.index.from]; @@ -1429,6 +1889,12 @@ ftfont_drive_otf (font, spec, in, from, to, out, adjustment) g->from = min_from; g->to = max_to; } + if (feature_idx) + { + unsigned int tag = features[feature_idx - 1].FeatureTag; + tag = PACK_OTF_TAG (tag); + g->internal = (g->internal & ~0x1FFFFFFF) | tag; + } for (i++, otfg++; (i < otf_gstring.used && otfg->f.index.from == otfg[-1].f.index.from); i++, otfg++) @@ -1441,15 +1907,342 @@ ftfont_drive_otf (font, spec, in, from, to, out, adjustment) g->code = otfg->glyph_id; g->measured = 0; } + feature_idx = otfg->positioning_type >> 4; + if (feature_idx) + { + unsigned int tag = features[feature_idx - 1].FeatureTag; + tag = PACK_OTF_TAG (tag); + g->internal = (g->internal & ~0x1FFFFFFF) | tag; + } out->used++; } } } - else + else if (gsub_features) { - if (out->allocated < out->used + len) - return -2; - for (i = 0; i < len; i++) + /* Just for checking which features will be applied. */ + if (OTF_drive_gsub_with_log (otf, &otf_gstring, script, langsys, + gsub_features) < 0) + goto simple_copy; + features = otf->gsub->FeatureList.Feature; + for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++, + otfg++) + { + int feature_idx = otfg->positioning_type >> 4; + + if (feature_idx) + { + unsigned int tag = features[feature_idx - 1].FeatureTag; + tag = PACK_OTF_TAG (tag); + for (j = otfg->f.index.from; j <= otfg->f.index.to; j++) + { + MFLTGlyph *g = in->glyphs + (from + j); + g->internal = (g->internal & ~0x1FFFFFFF) | tag; + } + } + } + } + else if (out) + { + if (out->allocated < out->used + len) + return -2; + for (i = 0; i < len; i++) + out->glyphs[out->used++] = in->glyphs[from + i]; + } + + if (gpos_features && out) + { + MFLTGlyph *base = NULL, *mark = NULL, *g; + int x_ppem, y_ppem, x_scale, y_scale; + + if (OTF_drive_gpos_with_log (otf, &otf_gstring, script, langsys, + gpos_features) < 0) + return to; + features = otf->gpos->FeatureList.Feature; + x_ppem = ft_face->size->metrics.x_ppem; + y_ppem = ft_face->size->metrics.y_ppem; + x_scale = ft_face->size->metrics.x_scale; + y_scale = ft_face->size->metrics.y_scale; + + for (i = 0, otfg = otf_gstring.glyphs, g = out->glyphs + gidx; + i < otf_gstring.used; i++, otfg++, g++) + { + MFLTGlyph *prev; + int feature_idx = otfg->positioning_type >> 4; + + if (feature_idx) + { + unsigned int tag = features[feature_idx - 1].FeatureTag; + tag = PACK_OTF_TAG (tag); + g->internal = (g->internal & ~0x1FFFFFFF) | tag; + } + + if (! otfg->glyph_id) + continue; + switch (otfg->positioning_type & 0xF) + { + case 0: + break; + case 1: /* Single */ + case 2: /* Pair */ + { + int format = otfg->f.f1.format; + + if (format & OTF_XPlacement) + adjustment[i].xoff + = otfg->f.f1.value->XPlacement * x_scale / 0x10000; + if (format & OTF_XPlaDevice) + adjustment[i].xoff + += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, x_ppem); + if (format & OTF_YPlacement) + adjustment[i].yoff + = - (otfg->f.f1.value->YPlacement * y_scale / 0x10000); + if (format & OTF_YPlaDevice) + adjustment[i].yoff + -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, y_ppem); + if (format & OTF_XAdvance) + adjustment[i].xadv + += otfg->f.f1.value->XAdvance * x_scale / 0x10000; + if (format & OTF_XAdvDevice) + adjustment[i].xadv + += DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, x_ppem); + if (format & OTF_YAdvance) + adjustment[i].yadv + += otfg->f.f1.value->YAdvance * y_scale / 0x10000; + if (format & OTF_YAdvDevice) + adjustment[i].yadv + += DEVICE_DELTA (otfg->f.f1.value->YAdvDevice, y_ppem); + adjustment[i].set = 1; + } + break; + case 3: /* Cursive */ + /* Not yet supported. */ + break; + case 4: /* Mark-to-Base */ + case 5: /* Mark-to-Ligature */ + if (! base) + break; + prev = base; + goto label_adjust_anchor; + default: /* i.e. case 6 Mark-to-Mark */ + if (! mark) + break; + prev = mark; + + label_adjust_anchor: + { + int base_x, base_y, mark_x, mark_y; + int this_from, this_to; + + base_x = otfg->f.f4.base_anchor->XCoordinate * x_scale / 0x10000; + base_y = otfg->f.f4.base_anchor->YCoordinate * y_scale / 0x10000; + mark_x = otfg->f.f4.mark_anchor->XCoordinate * x_scale / 0x10000; + mark_y = otfg->f.f4.mark_anchor->YCoordinate * y_scale / 0x10000; + + if (otfg->f.f4.base_anchor->AnchorFormat != 1) + adjust_anchor (ft_face, otfg->f.f4.base_anchor, + prev->code, x_ppem, y_ppem, &base_x, &base_y); + if (otfg->f.f4.mark_anchor->AnchorFormat != 1) + adjust_anchor (ft_face, otfg->f.f4.mark_anchor, g->code, + x_ppem, y_ppem, &mark_x, &mark_y); + adjustment[i].xoff = (base_x - mark_x); + adjustment[i].yoff = - (base_y - mark_y); + adjustment[i].back = (g - prev); + adjustment[i].xadv = 0; + adjustment[i].advance_is_absolute = 1; + adjustment[i].set = 1; + this_from = g->from; + this_to = g->to; + for (j = 0; prev + j < g; j++) + { + if (this_from > prev[j].from) + this_from = prev[j].from; + if (this_to < prev[j].to) + this_to = prev[j].to; + } + for (; prev <= g; prev++) + { + prev->from = this_from; + prev->to = this_to; + } + } + } + if (otfg->GlyphClass == OTF_GlyphClass0) + base = mark = g; + else if (otfg->GlyphClass == OTF_GlyphClassMark) + mark = g; + else + base = g; + } + } + else if (gpos_features) + { + if (OTF_drive_gpos_with_log (otf, &otf_gstring, script, langsys, + gpos_features) < 0) + return to; + features = otf->gpos->FeatureList.Feature; + for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; + i++, otfg++) + if (otfg->positioning_type & 0xF) + { + int feature_idx = otfg->positioning_type >> 4; + + if (feature_idx) + { + unsigned int tag = features[feature_idx - 1].FeatureTag; + tag = PACK_OTF_TAG (tag); + for (j = otfg->f.index.from; j <= otfg->f.index.to; j++) + { + MFLTGlyph *g = in->glyphs + (from + j); + g->internal = (g->internal & ~0x1FFFFFFF) | tag; + } + } + } + } + return to; + + simple_copy: + if (! out) + return to; + if (out->allocated < out->used + len) + return -2; + font->get_metrics (font, in, from, to); + memcpy (out->glyphs + out->used, in->glyphs + from, + sizeof (MFLTGlyph) * len); + out->used += len; + return to; +} + +static int +ftfont_try_otf (MFLTFont *font, MFLTOtfSpec *spec, + MFLTGlyphString *in, int from, int to) +{ + return ftfont_drive_otf (font, spec, in, from, to, NULL, NULL); +} + +#else /* not M17N_FLT_USE_NEW_FEATURE */ + +static int +ftfont_drive_otf (font, spec, in, from, to, out, adjustment) + MFLTFont *font; + MFLTOtfSpec *spec; + MFLTGlyphString *in; + int from, to; + MFLTGlyphString *out; + MFLTGlyphAdjustment *adjustment; +{ + struct MFLTFontFT *flt_font_ft = (struct MFLTFontFT *) font; + FT_Face ft_face = flt_font_ft->ft_face; + OTF *otf = flt_font_ft->otf; + int len = to - from; + int i, j, gidx; + OTF_Glyph *otfg; + char script[5], *langsys = NULL; + char *gsub_features = NULL, *gpos_features = NULL; + + if (len == 0) + return from; + OTF_tag_name (spec->script, script); + if (spec->langsys) + { + langsys = alloca (5); + OTF_tag_name (spec->langsys, langsys); + } + for (i = 0; i < 2; i++) + { + char *p; + + if (spec->features[i] && spec->features[i][1] != 0xFFFFFFFF) + { + for (j = 0; spec->features[i][j]; j++); + if (i == 0) + p = gsub_features = alloca (6 * j); + else + p = gpos_features = alloca (6 * j); + for (j = 0; spec->features[i][j]; j++) + { + if (spec->features[i][j] == 0xFFFFFFFF) + *p++ = '*', *p++ = ','; + else + { + OTF_tag_name (spec->features[i][j], p); + p[4] = ','; + p += 5; + } + } + *--p = '\0'; + } + } + + setup_otf_gstring (len); + for (i = 0; i < len; i++) + { + otf_gstring.glyphs[i].c = in->glyphs[from + i].c; + otf_gstring.glyphs[i].glyph_id = in->glyphs[from + i].code; + } + + OTF_drive_gdef (otf, &otf_gstring); + gidx = out->used; + + if (gsub_features) + { + if (OTF_drive_gsub (otf, &otf_gstring, script, langsys, gsub_features) + < 0) + goto simple_copy; + if (out->allocated < out->used + otf_gstring.used) + return -2; + for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; ) + { + MFLTGlyph *g; + int min_from, max_to; + int j; + + g = out->glyphs + out->used; + *g = in->glyphs[from + otfg->f.index.from]; + if (g->code != otfg->glyph_id) + { + g->c = 0; + g->code = otfg->glyph_id; + g->measured = 0; + } + out->used++; + min_from = g->from; + max_to = g->to; + if (otfg->f.index.from < otfg->f.index.to) + { + /* OTFG substitutes multiple glyphs in IN. */ + for (j = from + otfg->f.index.from + 1; + j <= from + otfg->f.index.to; j++) + { + if (min_from > in->glyphs[j].from) + min_from = in->glyphs[j].from; + if (max_to < in->glyphs[j].to) + max_to = in->glyphs[j].to; + } + g->from = min_from; + g->to = max_to; + } + for (i++, otfg++; (i < otf_gstring.used + && otfg->f.index.from == otfg[-1].f.index.from); + i++, otfg++) + { + g = out->glyphs + out->used; + *g = in->glyphs[from + otfg->f.index.to]; + if (g->code != otfg->glyph_id) + { + g->c = 0; + g->code = otfg->glyph_id; + g->measured = 0; + } + out->used++; + } + } + } + else + { + if (out->allocated < out->used + len) + return -2; + for (i = 0; i < len; i++) out->glyphs[out->used++] = in->glyphs[from + i]; } @@ -1532,7 +2325,7 @@ ftfont_drive_otf (font, spec, in, from, to, out, adjustment) base_x = otfg->f.f4.base_anchor->XCoordinate * x_scale / 0x10000; base_y = otfg->f.f4.base_anchor->YCoordinate * y_scale / 0x10000; mark_x = otfg->f.f4.mark_anchor->XCoordinate * x_scale / 0x10000; - mark_y = otfg->f.f4.mark_anchor->YCoordinate * y_scale / 0x10000;; + mark_y = otfg->f.f4.mark_anchor->YCoordinate * y_scale / 0x10000; if (otfg->f.f4.base_anchor->AnchorFormat != 1) adjust_anchor (ft_face, otfg->f.f4.base_anchor, @@ -1582,48 +2375,104 @@ ftfont_drive_otf (font, spec, in, from, to, out, adjustment) return to; } +#endif /* not M17N_FLT_USE_NEW_FEATURE */ + static MFLTGlyphString gstring; static int m17n_flt_initialized; extern Lisp_Object QCfamily; -Lisp_Object -ftfont_shape_by_flt (lgstring, font, ft_face, otf) +static Lisp_Object +ftfont_shape_by_flt (lgstring, font, ft_face, otf, matrix) Lisp_Object lgstring; struct font *font; FT_Face ft_face; OTF *otf; + FT_Matrix *matrix; { - EMACS_UINT len = LGSTRING_LENGTH (lgstring); + EMACS_UINT len = LGSTRING_GLYPH_LEN (lgstring); EMACS_UINT i; struct MFLTFontFT flt_font_ft; + MFLT *flt = NULL; + int with_variation_selector = 0; if (! m17n_flt_initialized) { M17N_INIT (); +#ifdef M17N_FLT_USE_NEW_FEATURE + mflt_enable_new_feature = 1; + mflt_try_otf = ftfont_try_otf; +#endif /* M17N_FLT_USE_NEW_FEATURE */ m17n_flt_initialized = 1; } for (i = 0; i < len; i++) - if (NILP (LGSTRING_GLYPH (lgstring, i))) - break; + { + Lisp_Object g = LGSTRING_GLYPH (lgstring, i); + int c; + + if (NILP (g)) + break; + c = LGLYPH_CHAR (g); + if (CHAR_VARIATION_SELECTOR_P (c)) + with_variation_selector++; + } len = i; + if (with_variation_selector) + { + setup_otf_gstring (len); + for (i = 0; i < len; i++) + { + Lisp_Object g = LGSTRING_GLYPH (lgstring, i); + + otf_gstring.glyphs[i].c = LGLYPH_CHAR (g); + otf_gstring.glyphs[i].f.index.from = LGLYPH_FROM (g); + otf_gstring.glyphs[i].f.index.to = LGLYPH_TO (g); + } + OTF_drive_cmap (otf, &otf_gstring); + for (i = 0; i < otf_gstring.used; i++) + { + OTF_Glyph *otfg = otf_gstring.glyphs + i; + Lisp_Object g0 = LGSTRING_GLYPH (lgstring, otfg->f.index.from); + Lisp_Object g1 = LGSTRING_GLYPH (lgstring, otfg->f.index.to); + + LGLYPH_SET_CODE (g0, otfg->glyph_id); + LGLYPH_SET_TO (g0, LGLYPH_TO (g1)); + LGSTRING_SET_GLYPH (lgstring, i, g0); + } + if (len > otf_gstring.used) + { + len = otf_gstring.used; + LGSTRING_SET_GLYPH (lgstring, len, Qnil); + } + } if (gstring.allocated == 0) { gstring.allocated = len * 2; gstring.glyph_size = sizeof (MFLTGlyph); - gstring.glyphs = malloc (sizeof (MFLTGlyph) * gstring.allocated); + gstring.glyphs = xmalloc (sizeof (MFLTGlyph) * gstring.allocated); } else if (gstring.allocated < len * 2) { gstring.allocated = len * 2; - gstring.glyphs = realloc (gstring.glyphs, - sizeof (MFLTGlyph) * gstring.allocated); + gstring.glyphs = xrealloc (gstring.glyphs, + sizeof (MFLTGlyph) * gstring.allocated); } + memset (gstring.glyphs, 0, sizeof (MFLTGlyph) * len); for (i = 0; i < len; i++) - gstring.glyphs[i].c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i)); + { + Lisp_Object g = LGSTRING_GLYPH (lgstring, i); + + gstring.glyphs[i].c = LGLYPH_CHAR (g); + if (with_variation_selector) + { + gstring.glyphs[i].code = LGLYPH_CODE (g); + gstring.glyphs[i].encoded = 1; + } + } + gstring.used = len; gstring.r2l = 0; @@ -1634,7 +2483,7 @@ ftfont_shape_by_flt (lgstring, font, ft_face, otf) flt_font_ft.flt_font.family = Mnil; else flt_font_ft.flt_font.family - = msymbol ((char *) SDATA (SYMBOL_NAME (family))); + = msymbol ((char *) SDATA (Fdowncase (SYMBOL_NAME (family)))); } flt_font_ft.flt_font.x_ppem = ft_face->size->metrics.x_ppem; flt_font_ft.flt_font.y_ppem = ft_face->size->metrics.y_ppem; @@ -1646,16 +2495,23 @@ ftfont_shape_by_flt (lgstring, font, ft_face, otf) flt_font_ft.font = font; flt_font_ft.ft_face = ft_face; flt_font_ft.otf = otf; + flt_font_ft.matrix = matrix->xx != 0 ? matrix : 0; + if (len > 1 + && gstring.glyphs[1].c >= 0x300 && gstring.glyphs[1].c <= 0x36F) + /* A little bit ad hoc. Perhaps, shaper must get script and + language information, and select a proper flt for them + here. */ + flt = mflt_get (msymbol ("combining")); for (i = 0; i < 3; i++) { - int result = mflt_run (&gstring, 0, len, &flt_font_ft.flt_font, NULL); + int result = mflt_run (&gstring, 0, len, &flt_font_ft.flt_font, flt); if (result != -2) break; gstring.allocated += gstring.allocated; - gstring.glyphs = realloc (gstring.glyphs, - sizeof (MFLTGlyph) * gstring.allocated); + gstring.glyphs = xrealloc (gstring.glyphs, + sizeof (MFLTGlyph) * gstring.allocated); } - if (gstring.used > LGSTRING_LENGTH (lgstring)) + if (gstring.used > LGSTRING_GLYPH_LEN (lgstring)) return Qnil; for (i = 0; i < gstring.used; i++) { @@ -1704,72 +2560,131 @@ ftfont_shape (lgstring) { struct font *font; struct ftfont_info *ftfont_info; + OTF *otf; CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring), font); ftfont_info = (struct ftfont_info *) font; - if (! ftfont_info->maybe_otf) + otf = ftfont_get_otf (ftfont_info); + if (! otf) return make_number (0); - if (! ftfont_info->otf) - { - OTF *otf = OTF_open_ft_face (ftfont_info->ft_size->face); + return ftfont_shape_by_flt (lgstring, font, ftfont_info->ft_size->face, otf, + &ftfont_info->matrix); +} - if (! otf || OTF_get_table (otf, "head") < 0) - { - if (otf) - OTF_close (otf); - ftfont_info->maybe_otf = 0; - return make_number (0); - } +#endif /* HAVE_M17N_FLT */ - ftfont_info->otf = otf; - } +#ifdef HAVE_OTF_GET_VARIATION_GLYPHS - return ftfont_shape_by_flt (lgstring, font, ftfont_info->ft_size->face, - ftfont_info->otf); +static int +ftfont_variation_glyphs (font, c, variations) + struct font *font; + int c; + unsigned variations[256]; +{ + struct ftfont_info *ftfont_info = (struct ftfont_info *) font; + OTF *otf = ftfont_get_otf (ftfont_info); + + if (! otf) + return 0; + return OTF_get_variation_glyphs (otf, c, variations); } -#endif /* HAVE_M17N_FLT */ +#endif /* HAVE_OTF_GET_VARIATION_GLYPHS */ #endif /* HAVE_LIBOTF */ Lisp_Object -ftfont_font_format (FcPattern *pattern) +ftfont_font_format (FcPattern *pattern, Lisp_Object filename) { FcChar8 *str; #ifdef FC_FONTFORMAT - if (FcPatternGetString (pattern, FC_FONTFORMAT, 0, &str) != FcResultMatch) - return Qnil; - if (strcmp ((char *) str, "TrueType") == 0) - return intern ("truetype"); - if (strcmp ((char *) str, "Type 1") == 0) - return intern ("type1"); - if (strcmp ((char *) str, "PCF") == 0) - return intern ("pcf"); - if (strcmp ((char *) str, "BDF") == 0) - return intern ("bdf"); -#else /* not FC_FONTFORMAT */ - int len; - - if (FcPatternGetString (pattern, FC_FILE, 0, &str) != FcResultMatch) - return Qnil; - len = strlen ((char *) str); - if (len >= 4) + if (pattern) { - str += len - 4; - if (xstrcasecmp ((char *) str, ".ttf") == 0) + if (FcPatternGetString (pattern, FC_FONTFORMAT, 0, &str) != FcResultMatch) + return Qnil; + if (strcmp ((char *) str, "TrueType") == 0) return intern ("truetype"); - if (xstrcasecmp ((char *) str, "pfb") == 0) + if (strcmp ((char *) str, "Type 1") == 0) return intern ("type1"); - if (xstrcasecmp ((char *) str, "pcf") == 0) + if (strcmp ((char *) str, "PCF") == 0) return intern ("pcf"); - if (xstrcasecmp ((char *) str, "bdf") == 0) + if (strcmp ((char *) str, "BDF") == 0) return intern ("bdf"); } -#endif /* not FC_FONTFORMAT */ +#endif /* FC_FONTFORMAT */ + if (STRINGP (filename)) + { + int len = SBYTES (filename); + + if (len >= 4) + { + str = (FcChar8 *) (SDATA (filename) + len - 4); + if (xstrcasecmp ((char *) str, ".ttf") == 0) + return intern ("truetype"); + if (xstrcasecmp ((char *) str, ".pfb") == 0) + return intern ("type1"); + if (xstrcasecmp ((char *) str, ".pcf") == 0) + return intern ("pcf"); + if (xstrcasecmp ((char *) str, ".bdf") == 0) + return intern ("bdf"); + } + } return intern ("unknown"); } - +static const char *ftfont_booleans [] = { + ":antialias", + ":hinting", + ":verticallayout", + ":autohint", + ":globaladvance", + ":outline", + ":scalable", + ":minspace", + ":embolden", + NULL, +}; + +static const char *ftfont_non_booleans [] = { + ":family", + ":familylang", + ":style", + ":stylelang", + ":fullname", + ":fullnamelang", + ":slant", + ":weight", + ":size", + ":width", + ":aspect", + ":pixelsize", + ":spacing", + ":foundry", + ":hintstyle", + ":file", + ":index", + ":ftface", + ":rasterizer", + ":scale", + ":dpi", + ":rgba", + ":lcdfilter", + ":charset", + ":lang", + ":fontversion", + ":capability", + NULL, +}; + +static void +ftfont_filter_properties (font, alist) + Lisp_Object font; + Lisp_Object alist; +{ + font_filter_properties (font, alist, ftfont_booleans, ftfont_non_booleans); +} + + void syms_of_ftfont () {