+ for (i = mtext_nchars (mt) - 1; i >= 0; i--)
+ if (! FcCharSetAddChar (cs, (FcChar32) mtext_ref_char (mt, i)))
+ {
+ FcCharSetDestroy (cs);
+ return NULL;
+ }
+ if (mtext_nchars (mt) > 0
+ && (mt = mtext_get_prop (mt, 0, Mtext)))
+ for (i = mtext_nchars (mt) - 1; i >= 0; i--)
+ if (! FcCharSetAddChar (cs, (FcChar32) mtext_ref_char (mt, i)))
+ {
+ FcCharSetDestroy (cs);
+ return NULL;
+ }
+ }
+ return cs;
+}
+
+#else /* not HAVE_FONTCONFIG */
+
+static MPlist *
+ft_add_font (char *filename)
+{
+ FT_Face ft_face;
+ char *stylename;
+ int size = 0;
+ MSymbol family;
+ MFontFT *ft_info;
+ MFont *font;
+ MPlist *plist;
+ int i;
+ char *buf;
+ int bufsize = 0;
+
+ if (FT_New_Face (ft_library, filename, 0, &ft_face) != 0)
+ return NULL;
+ ft_info = ft_gen_font (ft_face);
+ FT_Done_Face (ft_face);
+ if (! ft_info)
+ return NULL;
+
+ font = &ft_info->font;
+ font->file = msymbol (filename);
+
+ plist = mplist_find_by_key (ft_font_list, family);
+ if (plist)
+ mplist_push (MPLIST_PLIST (plist), font->file, ft_info);
+ else
+ {
+ plist = mplist ();
+ mplist_add (plist, font->file, ft_info);
+ plist = mplist_push (ft_font_list, family, plist);
+ }
+ return plist;
+}
+
+static void
+ft_init_font_list (void)
+{
+ MPlist *plist;
+ struct stat buf;
+ char *pathname;
+ char *path;
+ USE_SAFE_ALLOCA;
+
+ ft_font_list = mplist ();
+ MPLIST_DO (plist, mfont_freetype_path)
+ if (MPLIST_STRING_P (plist)
+ && (pathname = MPLIST_STRING (plist))
+ && stat (pathname, &buf) == 0)
+ {
+ if (S_ISREG (buf.st_mode))
+ ft_add_font (pathname);
+ else if (S_ISDIR (buf.st_mode))
+ {
+ DIR *dir = opendir (pathname);
+
+ if (dir)
+ {
+ int len = strlen (pathname);
+ struct dirent *dp;
+
+ while ((dp = readdir (dir)) != NULL)
+ {
+ SAFE_ALLOCA (path, len + strlen (dp->d_name) + 2);
+ strcpy (path, pathname);
+ path[len] = '/';
+ strcpy (path + len + 1, dp->d_name);
+ ft_add_font (path);
+ }
+ closedir (dir);
+ }
+ }
+ }
+ SAFE_FREE (path);
+}
+
+/* Return 1 iff the font pointed by FT_INFO has all characters in
+ CHAR_LIST. */
+
+static int
+ft_has_char_list_p (MFontFT *ft_info, MPlist *char_list)
+{
+ FT_Face ft_face;
+ MPlist *cl;
+
+ if (FT_New_Face (ft_library, MSYMBOL_NAME (ft_info->font.file), 0, &ft_face))
+ return 0;
+ MPLIST_DO (cl, char_list)
+ if (FT_Get_Char_Index (ft_face, (FT_ULong) MPLIST_INTEGER (cl)) == 0)
+ break;
+ FT_Done_Face (ft_face);
+ return MPLIST_TAIL_P (cl);
+}
+
+/* Return ((FAMILY . FONT) ...) where FONT is a pointer to MFontFT
+ that supports characters in CHAR_LIST or MT. One of CHAR_LIST or
+ MT must be NULL. */
+
+static MPlist *
+ft_list_char_list (MPlist *char_list, MText *mt)
+{
+ MPlist *plist = NULL, *pl, *p;
+
+ if (! ft_font_list)
+ ft_list_family (Mnil, 0);
+
+ if (mt)
+ {
+ int len = mtext_nchars (mt);
+ MText *extra = mtext_get_prop (mt, 0, Mtext);
+ int total_len = len + (extra ? mtext_nchars (extra) : 0);
+ int i;
+
+ char_list = mplist ();
+ for (i = 0; i < total_len; i++)
+ {
+ int c = (i < len ? mtext_ref_char (mt, i)
+ : mtext_ref_char (extra, i - len));
+
+ if (! mplist_find_by_value (char_list, (void *) c))
+ mplist_push (char_list, Minteger, (void *) c);
+ }
+ }
+
+ MPLIST_DO (pl, ft_font_list)
+ {
+ MPLIST_DO (p, MPLIST_PLIST (pl))
+ {
+ MFontFT *ft_info = MPLIST_VAL (p);
+
+ if (ft_has_char_list_p (ft_info, char_list))
+ {
+ MSymbol family = mfont_get_prop (&ft_info->font, Mfamily);
+
+ if (! plist)
+ plist = mplist ();
+ mplist_push (plist, family, ft_info);
+ }
+ }
+ }
+ if (mt)
+ M17N_OBJECT_UNREF (char_list);
+ return plist;
+}
+#endif /* not HAVE_FONTCONFIG */
+
+
+/* Return an element of ft_font_list for FAMILY. If FAMILY is Mnil,
+ scan all fonts and return ft_font_list. */
+
+static MPlist *
+ft_list_family (MSymbol family, int check_generic)
+{
+ MPlist *plist;
+#ifdef HAVE_FONTCONFIG
+ char *fam;
+ MPlist *pl, *p;
+ FcPattern *pattern;
+ FcObjectSet *os;
+ FcFontSet *fs;
+ int i;
+ char *buf;
+ int bufsize = 0;
+ MSymbol generic;
+
+ if (! ft_font_list)
+ {
+ MSymbol sym;
+
+ plist = ft_font_list = mplist ();
+ pattern = FcPatternCreate ();
+ os = FcObjectSetBuild (FC_FAMILY, NULL);
+ fs = FcFontList (fc_config, pattern, os);
+ for (i = 0; i < fs->nfont; i++)
+ {
+ if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
+ (FcChar8 **) &fam) != FcResultMatch)
+ continue;
+ STRDUP_LOWER (buf, bufsize, fam);
+ sym = msymbol (buf);
+ if (! mplist_find_by_key (ft_font_list, sym))
+ plist = mplist_add (plist, sym, NULL);
+ }
+ FcFontSetDestroy (fs);
+ FcObjectSetDestroy (os);
+ FcPatternDestroy (pattern);
+ }
+
+ if (family == Mnil)
+ {
+ if (! all_fonts_scaned)
+ {
+ MPLIST_DO (plist, ft_font_list)
+ {
+ if (! MPLIST_VAL (plist))
+ ft_list_family (MPLIST_KEY (plist), 0);
+ }
+ all_fonts_scaned = 1;
+ }
+ return ft_font_list;
+ }
+
+ plist = mplist_find_by_key (ft_font_list, family);
+ if (plist)
+ {
+ if (! MPLIST_VAL (plist))
+ {
+ fam = MSYMBOL_NAME (family);
+ pattern = FcPatternCreate ();
+ FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) fam);
+ os = FcObjectSetBuild (FC_FOUNDRY, FC_WEIGHT, FC_SLANT, FC_WIDTH,
+ FC_PIXEL_SIZE, FC_LANG, FC_CHARSET, FC_FILE,
+ NULL);
+ fs = FcFontList (fc_config, pattern, os);
+ p = pl = mplist ();
+ for (i = 0; i < fs->nfont; i++)
+ {
+ MFontFT *ft_info = fc_gen_font (fs->fonts[i], fam);
+ p = mplist_add (p, ft_info->font.file, ft_info);
+ }
+ MPLIST_VAL (plist) = pl;
+ FcFontSetDestroy (fs);
+ FcObjectSetDestroy (os);
+ FcPatternDestroy (pattern);
+ }
+ }
+ else if (check_generic
+ && (generic = msymbol_get (family, Mgeneric_family)) != Mnil)
+ {
+ /* Check if FAMILY is a geneneric family (e.g. `serif'). */
+ FcChar8 *fam8;
+
+ if (family != generic)
+ plist = ft_list_family (generic, 1);
+ else
+ {
+ fam = MSYMBOL_NAME (family);
+ plist = mplist ();
+ mplist_push (ft_font_list, family, plist);
+ pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, fam, NULL);
+ FcConfigSubstitute (fc_config, pattern, FcMatchPattern);
+ for (i = 0; 1; i++)
+ {
+ if (FcPatternGetString (pattern, FC_FAMILY, i, &fam8)
+ != FcResultMatch)
+ break;
+ STRDUP_LOWER (buf, bufsize, (char *) fam8);
+ family = msymbol (buf);
+ if (msymbol_get (family, Mgeneric_family))
+ break;
+ pl = ft_list_family (family, 0);
+ if (! pl)
+ continue;
+ MPLIST_DO (pl, MPLIST_PLIST (pl))
+ plist = mplist_add (plist, Mt, MPLIST_VAL (pl));
+ }
+ plist = ft_font_list;
+ }
+ }
+ else
+ {
+ /* Check if there exist an alias. */
+ pl = mplist ();
+ plist = mplist_add (ft_font_list, family, pl);
+
+ pattern = FcPatternBuild (NULL,
+ FC_FAMILY, FcTypeString, MSYMBOL_NAME (family),
+ NULL);
+ FcConfigSubstitute (fc_config, pattern, FcMatchPattern);
+
+ for (i = 0; FcPatternGetString (pattern, FC_FAMILY, i,
+ (FcChar8 **) &fam) == FcResultMatch;
+ i++);
+ if (i > 0)
+ {
+ /* The last one is a generic family. */
+ MSymbol sym;
+ int j;
+ FcPattern *pat = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, fam,
+ NULL);
+
+ FcConfigSubstitute (fc_config, pat, FcMatchPattern);
+ for (j = 0; FcPatternGetString (pat, FC_FAMILY, j,
+ (FcChar8 **) &fam) == FcResultMatch;
+ j++);
+
+ /* Now we know that the last J fonts in PATTERN are from
+ generic font, and the first one is not available. So,
+ the remaining ones are aliases. */
+ j = i - j;
+ for (i = 1; i < j; i++)
+ {
+ FcPatternGetString (pattern, FC_FAMILY, i, (FcChar8 **) &fam);
+ STRDUP_LOWER (buf, bufsize, fam);
+ sym = msymbol (buf);
+ p = MPLIST_PLIST (ft_list_family (sym, 0));
+ if (! MPLIST_TAIL_P (p))
+ MPLIST_DO (p, p)
+ mplist_push (pl, Mt, MPLIST_VAL (p));
+ }
+ }
+ }
+
+#else /* not HAVE_FONTCONFIG */
+
+ if (! all_fonts_scaned)
+ {
+ ft_init_font_list ();
+ all_fonts_scaned = 1;
+ }
+ if (family == Mnil)
+ plist = ft_font_list;
+ else
+ {
+ plist = mplist_find_by_key (ft_font_list, family);
+ if (! plist)
+ plist = mplist_push (ft_font_list, family, mplist ());
+ }
+#endif /* not HAVE_FONTCONFIG */
+
+ return plist;
+}
+
+static MPlist *
+ft_list_language (MSymbol language)
+{
+ MPlist *plist = NULL;
+ MText *mt;
+
+ if (! ft_language_list)
+ ft_language_list = mplist ();
+ else if ((plist = mplist_find_by_key (ft_language_list, language)))
+ return (MPLIST_VAL (plist) ? MPLIST_PLIST (plist) : NULL);
+
+ mt = mlanguage_text (language);
+
+#ifdef HAVE_FONTCONFIG
+ {
+ FcPattern *pattern = NULL;
+ FcCharSet *cs = NULL;
+ FcLangSet *ls = NULL;
+
+ if (! (pattern = FcPatternCreate ()))
+ goto err;
+
+ if (mt && mtext_nchars (mt) > 0)
+ {
+ cs = fc_build_charset (NULL, mt);
+ if (cs && ! FcPatternAddCharSet (pattern, FC_CHARSET, cs))
+ goto err;
+ }
+ else
+ {
+ if (! (ls = FcLangSetCreate ()))
+ goto err;
+ if (! FcLangSetAdd (ls, (FcChar8 *) MSYMBOL_NAME (language))
+ || ! FcPatternAddLangSet (pattern, FC_LANG, ls))
+ goto err;
+ }
+
+ plist = fc_list_pattern (pattern);
+ err:
+ if (cs) FcCharSetDestroy (cs);
+ if (ls) FcLangSetDestroy (ls);
+ if (pattern) FcPatternDestroy (pattern);
+ }
+#else /* not HAVE_FONTCONFIG */
+ if (mt && mtext_nchars (mt) > 0)
+ plist = ft_list_char_list (NULL, mt);
+#endif /* not HAVE_FONTCONFIG */
+
+ mplist_push (ft_language_list, language, plist);
+ return plist;
+}
+
+static MPlist *
+ft_list_script (MSymbol script)
+{
+ MPlist *plist = NULL;
+ MPlist *char_list;
+
+ if (! ft_script_list)
+ ft_script_list = mplist ();
+ else if ((plist = mplist_find_by_key (ft_script_list, script)))
+ return (MPLIST_VAL (plist) ? MPLIST_PLIST (plist) : NULL);
+
+ char_list = mscript__char_list (script);
+
+#ifdef HAVE_FONTCONFIG
+ if (char_list)
+ {
+ FcPattern *pattern = NULL;
+ FcCharSet *cs;
+
+ if (! (pattern = FcPatternCreate ()))
+ goto err;
+ cs = fc_build_charset (char_list, NULL);
+ if (cs && ! FcPatternAddCharSet (pattern, FC_CHARSET, cs))
+ goto err;
+ plist = fc_list_pattern (pattern);
+ err:
+ if (cs) FcCharSetDestroy (cs);
+ if (pattern) FcPatternDestroy (pattern);
+ }
+#else /* not HAVE_FONTCONFIG */
+ if (char_list)
+ plist = ft_list_char_list (char_list, NULL);
+#endif /* not HAVE_FONTCONFIG */
+
+ mplist_push (ft_script_list, script, plist);
+ return (plist);
+}
+
+static int
+ft_check_cap_otf (MFontFT *ft_info, MFontCapability *cap, FT_Face ft_face)
+{
+#ifdef HAVE_OTF
+ if (ft_info->otf == invalid_otf)
+ return -1;
+ if (! ft_info->otf)
+ {
+#if (LIBOTF_MAJOR_VERSION > 0 || LIBOTF_MINOR_VERSION > 9 || LIBOTF_RELEASE_NUMBER > 4)
+ if (ft_face)
+ ft_info->otf = OTF_open_ft_face (ft_face);
+ else
+#endif
+ ft_info->otf = OTF_open (MSYMBOL_NAME (ft_info->font.file));
+ if (! ft_info->otf)
+ {
+ ft_info->otf = invalid_otf;
+ return -1;
+ }
+ }
+ if (cap->features[MFONT_OTT_GSUB].nfeatures
+ && cap->features[MFONT_OTT_GSUB].tags[0]
+ && (OTF_check_features
+ (ft_info->otf, 1,
+ cap->script_tag, cap->langsys_tag,
+ cap->features[MFONT_OTT_GSUB].tags,
+ cap->features[MFONT_OTT_GSUB].nfeatures) != 1))
+ return -1;
+ if (cap->features[MFONT_OTT_GPOS].nfeatures
+ && cap->features[MFONT_OTT_GPOS].tags[0]
+ && (OTF_check_features
+ (ft_info->otf, 0,
+ cap->script_tag, cap->langsys_tag,
+ cap->features[MFONT_OTT_GPOS].tags,
+ cap->features[MFONT_OTT_GPOS].nfeatures) != 1))
+ return -1;
+ return 0;
+#else /* not HAVE_OTF */
+ return -1;
+#endif /* not HAVE_OTF */
+}
+
+static int
+ft_check_language (MFontFT *ft_info, MSymbol language, FT_Face ft_face)
+{
+ MText *mt;
+ MText *extra;
+ int ft_face_allocaed = 0;
+ int len, total_len;
+ int i;
+
+#ifdef HAVE_FONTCONFIG
+ if (ft_info->langset
+ && (FcLangSetHasLang (ft_info->langset,
+ (FcChar8 *) MSYMBOL_NAME (language))
+ != FcLangDifferentLang))
+ return 0;
+#endif /* HAVE_FONTCONFIG */
+
+ mt = mlanguage_text (language);
+ if (! mt || mtext_nchars (mt) == 0)
+ return -1;
+
+ if (! ft_face)
+ {
+ char *filename = MSYMBOL_NAME (ft_info->font.file);
+
+ if (FT_New_Face (ft_library, filename, 0, &ft_face))
+ return -1;
+ ft_face_allocaed = 1;
+ }
+
+ len = mtext_nchars (mt);
+ extra = mtext_get_prop (mt, 0, Mtext);
+ total_len = len + (extra ? mtext_nchars (extra) : 0);
+
+ for (i = 0; i < total_len; i++)
+ {
+ int c = (i < len ? mtext_ref_char (mt, i)
+ : mtext_ref_char (extra, i - len));
+
+#ifdef HAVE_FONTCONFIG
+ if (ft_info->charset
+ && FcCharSetHasChar (ft_info->charset, (FcChar32) c) == FcFalse)
+ break;
+#endif /* HAVE_FONTCONFIG */
+ if (FT_Get_Char_Index (ft_face, (FT_ULong) c) == 0)
+ break;
+ }
+
+ if (ft_face_allocaed)
+ FT_Done_Face (ft_face);
+
+ return (i == total_len ? 0 : -1);
+}
+
+static int
+ft_check_script (MFontFT *ft_info, MSymbol script, FT_Face ft_face)
+{
+ MPlist *char_list = mscript__char_list (script);
+
+ if (! char_list)
+ return -1;
+#ifdef HAVE_FONTCONFIG
+ if (ft_info->charset)
+ {
+ MPLIST_DO (char_list, char_list)
+ if (FcCharSetHasChar (ft_info->charset,
+ (FcChar32) MPLIST_INTEGER (char_list)) == FcFalse)
+ break;
+ }
+ else
+#endif /* HAVE_FONTCONFIG */
+ {
+ int ft_face_allocaed = 0;
+
+ if (! ft_face)
+ {
+ char *filename = MSYMBOL_NAME (ft_info->font.file);
+
+ if (FT_New_Face (ft_library, filename, 0, &ft_face))
+ return -1;
+ ft_face_allocaed = 1;
+ }
+
+ MPLIST_DO (char_list, char_list)
+ if (FT_Get_Char_Index (ft_face, (FT_ULong) MPLIST_INTEGER (char_list))
+ == 0)
+ break;
+ if (ft_face_allocaed)
+ FT_Done_Face (ft_face);
+ }
+
+ return (MPLIST_TAIL_P (char_list) ? 0 : -1);
+}
+
+static MPlist *ft_default_list;
+
+static MPlist *
+ft_list_default ()
+{
+ if (ft_default_list)
+ return ft_default_list;
+ ft_default_list = mplist ();
+#ifdef HAVE_FONTCONFIG
+ {
+ FcPattern *pat = FcPatternCreate ();
+ FcChar8 *fam;
+ char *buf;
+ int bufsize = 0;
+ int i;
+
+ FcConfigSubstitute (fc_config, pat, FcMatchPattern);
+ for (i = 0; FcPatternGetString (pat, FC_FAMILY, i, &fam) == FcResultMatch;
+ i++)
+ {
+ MSymbol family;
+ MPlist *plist;
+
+ STRDUP_LOWER (buf, bufsize, (char *) fam);
+ family = msymbol (buf);
+ if (msymbol_get (family, Mgeneric_family))
+ continue;
+ plist = MPLIST_PLIST (ft_list_family (family, 0));
+ MPLIST_DO (plist, plist)
+ mplist_add (ft_default_list, family, MPLIST_VAL (plist));
+ }
+ }
+#else /* not HAVE_FONTCONFIG */
+ {
+ MPlist *plist, *pl;
+
+ MPLIST_DO (plist, ft_list_family (Mnil, 0))
+ {
+ pl = MPLIST_PLIST (plist);
+ if (! MPLIST_TAIL_P (pl))
+ mplist_add (ft_default_list, MPLIST_KEY (plist), pl);
+ }
+ }
+#endif /* not HAVE_FONTCONFIG */
+ return ft_default_list;
+}
+
+
+static MPlist *ft_capability_list;
+
+static MPlist *
+ft_list_capability (MSymbol capability)
+{
+ MFontCapability *cap;
+ MPlist *plist = NULL, *pl;
+
+ if (! ft_capability_list)
+ ft_capability_list = mplist ();
+ else if ((plist = mplist_find_by_key (ft_capability_list, capability)))
+ return (MPLIST_VAL (plist) ? MPLIST_VAL (plist) : NULL);
+
+ cap = mfont__get_capability (capability);
+
+ if (cap && cap->language != Mnil)
+ {
+ plist = ft_list_language (cap->language);
+ if (! plist)
+ return NULL;
+ plist = mplist_copy (plist);
+ }
+
+ if (cap && cap->script != Mnil)
+ {
+ if (! plist)
+ {
+ plist = ft_list_script (cap->script);
+ if (! plist)
+ return NULL;
+ plist = mplist_copy (plist);
+ }
+ else
+ {
+ for (pl = plist; ! MPLIST_TAIL_P (pl);)
+ {
+ if (ft_check_script (MPLIST_VAL (pl), cap->script, NULL) < 0)
+ mplist_pop (pl);
+ else
+ pl = MPLIST_NEXT (pl);
+ }
+ }
+
+ if (cap->script_tag)
+ {
+ for (pl = plist; ! MPLIST_TAIL_P (pl);)
+ {
+ if (ft_check_cap_otf (MPLIST_VAL (pl), cap, NULL) < 0)
+ mplist_pop (pl);
+ else
+ pl = MPLIST_NEXT (pl);
+ }
+ }
+
+ if (MPLIST_TAIL_P (plist))
+ {
+ M17N_OBJECT_UNREF (plist);
+ plist = NULL;
+ }
+ }
+
+ mplist_push (ft_capability_list, capability, plist);
+ return plist;
+}
+
+
+static MPlist *
+ft_list_file (MSymbol filename)
+{
+ MPlist *plist = NULL;
+
+ if (! ft_file_list)
+ ft_file_list = mplist ();
+ else if ((plist = mplist_find_by_key (ft_file_list, filename)))
+ return (MPLIST_VAL (plist) ? MPLIST_PLIST (plist) : NULL);
+
+#ifdef HAVE_FONTCONFIG
+ {
+ FcPattern *pattern = FcPatternCreate ();
+ FcObjectSet *os;
+ FcFontSet *fs;
+
+ FcPatternAddString (pattern, FC_FILE, (FcChar8 *) MSYMBOL_NAME (filename));
+ os = FcObjectSetBuild (FC_FAMILY, NULL);
+ fs = FcFontList (fc_config, pattern, os);
+ if (fs->nfont > 0)
+ {
+ char *fam;
+ char *buf;
+ int bufsize = 0;
+
+ if (FcPatternGetString (fs->fonts[0], FC_FAMILY, 0,
+ (FcChar8 **) &fam) == FcResultMatch)
+ {
+ MSymbol family;
+ MPlist *pl;
+
+ STRDUP_LOWER (buf, bufsize, fam);
+ family = msymbol (buf);
+ pl = ft_list_family (family, 0);
+ MPLIST_DO (pl, MPLIST_PLIST (pl))