+}
+
+static MPlist *
+ft_list_script (MSymbol script)
+{
+ MPlist *plist = NULL;
+ MPlist *language_list, *pl;
+
+ 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);
+
+ language_list = mlanguage__list (script);
+ MPLIST_DO (pl, language_list)
+ {
+ MSymbol language = MPLIST_VAL (pl) ? MPLIST_SYMBOL (pl) : MPLIST_KEY (pl);
+ MPlist *p = ft_list_language (language);
+ MSymbol family;
+
+ if (! p)
+ continue;
+ if (! plist)
+ plist = mplist ();
+ MPLIST_DO (p, p)
+ {
+ family = MPLIST_KEY (p);
+ if (! mplist_find_by_value (plist, MPLIST_VAL (p)))
+ mplist_add (plist, family, MPLIST_VAL (p));
+ }
+ }
+ mplist_push (ft_script_list, script, plist);
+ M17N_OBJECT_UNREF (language_list);
+ return (plist);
+}
+
+static int
+ft_check_otf (MFontFT *ft_info, MFontCapability *cap)
+{
+ if (ft_info->otf == invalid_otf)
+ return -1;
+ if (! ft_info->otf)
+ {
+ 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;
+}
+
+static int
+ft_check_lang (MFontFT *ft_info, MFontCapability *cap)
+{
+#ifdef HAVE_FONTCONFIG
+ MPlist *plist;
+ MText *mt;
+ int i, j;
+
+ for (i = 0; cap->lang[i] != Mnil; i++)
+ {
+ if (ft_info->lang
+ && (plist = mplist_find_by_key (ft_info->lang, cap->lang[i])))
+ {
+ if (MPLIST_VAL (plist))
+ return 0;
+ continue;
+ }
+
+ if (! ft_info->langset)
+ {
+ FcPattern *pat = FcPatternBuild (NULL, FC_FILE, FcTypeString,
+ MSYMBOL_NAME (ft_info->font.file),
+ NULL);
+ FcObjectSet *os = FcObjectSetBuild (FC_LANG, FC_CHARSET, NULL);
+ FcFontSet *fs = FcFontList (fc_config, pat, os);
+
+ if (fs->nfont == 0)
+ return -1;
+ if (FcPatternGetLangSet (fs->fonts[0], FC_LANG, 0, &ft_info->langset)
+ == FcResultMatch)
+ ft_info->langset = FcLangSetCopy (ft_info->langset);
+ else
+ ft_info->langset = FcLangSetCreate ();
+ FcPatternGetCharSet (fs->fonts[0], FC_CHARSET, 0, &ft_info->charset);
+ FcFontSetDestroy (fs);
+ FcObjectSetDestroy (os);
+ FcPatternDestroy (pat);
+ }
+ if (! ft_info->lang)
+ ft_info->lang = mplist ();
+ if (FcLangSetHasLang (ft_info->langset,
+ (FcChar8 *) MSYMBOL_NAME (cap->lang[i]))
+ == FcLangEqual)
+ {
+ mplist_push (ft_info->lang, cap->lang[i], Mt);
+ return 0;
+ }
+
+ mt = msymbol_get (cap->lang[i], Mtext);
+ if (! mt)
+ {
+ mplist_push (ft_info->lang, cap->lang[i], Mnil);
+ continue;
+ }
+
+ for (j = mtext_nchars (mt) - 1; j >= 0; j--)
+ if (! FcCharSetAddChar (ft_info->charset,
+ (FcChar32) mtext_ref_char (mt, j)))
+ break;
+ mplist_push (ft_info->lang, cap->lang[i], (j < 0 ? Mt : Mnil));
+ if (j < 0)
+ return 0;
+ }
+#endif /* HAVE_FONTCONFIG */
+ return -1;
+}
+
+static MPlist *ft_capability_list;
+
+static MPlist *
+ft_list_capability (MSymbol sym)
+{
+ MPlist *plist, *pl, *p;
+ MFontCapability *cap = mfont__get_capability (sym);
+
+ if (! cap)
+ return NULL;
+ if (ft_capability_list)
+ {
+ plist = mplist_find_by_key (ft_capability_list, sym);
+ if (plist)
+ return (MPLIST_VAL (plist) ? MPLIST_VAL (plist) : NULL);
+ }
+ else
+ {
+ plist = NULL;
+ ft_capability_list = mplist ();
+ }
+
+ if (cap->script != Mnil)
+ {
+ pl = ft_list_script (cap->script);
+ if (pl)
+ MPLIST_DO (pl, pl)
+ {
+ if (cap->script_tag && ft_check_otf (MPLIST_VAL (pl), cap) < 0)
+ continue;
+ if (cap->lang && ft_check_lang (MPLIST_VAL (pl), cap) < 0)
+ continue;
+ if (! plist)
+ plist = mplist ();
+ mplist_add (plist, MPLIST_KEY (pl), MPLIST_VAL (pl));
+ }
+ mplist_push (ft_capability_list, sym, plist);
+ return plist;
+ }
+
+ if (cap->lang)
+ {
+ int i;
+
+ for (i = 0; cap->lang[i] != Mnil; i++)
+ {
+ p = ft_list_language (cap->lang[i]);
+ if (p)
+ {
+ if (! plist)
+ plist = mplist ();
+ MPLIST_DO (p, p)
+ mplist_add (plist, MPLIST_KEY (p), MPLIST_VAL (p));
+ }
+ }
+ }
+ mplist_push (ft_capability_list, sym, 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))
+ {
+ MFontFT *ft_info = MPLIST_VAL (pl);
+
+ if (ft_info->font.file == filename)
+ {
+ plist = mplist ();
+ mplist_add (plist, family, ft_info);
+ break;
+ }
+ }
+ }
+ }
+ }
+#else /* not HAVE_FONTCONFIG */
+ {
+ MPlist *pl, *p;
+
+ MPLIST_DO (pl, ft_list_family (Mnil, 0))
+ {
+ MPLIST_DO (p, MPLIST_PLIST (pl))
+ {
+ MFontFT *ft_info = MPLIST_VAL (pl);
+
+ if (ft_info->font.file == filename)
+ {
+ plist = mplist ();
+ mplist_add (plist, MPLIST_KEY (pl), ft_info);
+ break;
+ }
+ }
+ if (plist)
+ break;
+ }
+ }
+#endif /* not HAVE_FONTCONFIG */