+typedef struct
+{
+ MFont font;
+#ifdef HAVE_OTF
+ /* NULL if not yet opened. invalid_otf if not OTF. */
+ OTF *otf;
+#endif /* HAVE_OTF */
+#ifdef HAVE_FONTCONFIG
+ FcLangSet *langset;
+ FcCharSet *charset;
+#endif /* HAVE_FONTCONFIG */
+} MFontFT;
+
+typedef struct
+{
+ M17NObject control;
+ FT_Face ft_face; /* This must be the 2nd member. */
+ MPlist *charmap_list;
+ int face_encapsulated;
+} MRealizedFontFT;
+
+typedef struct
+{
+ char *ft_style;
+ int len;
+ enum MFontProperty prop;
+ char *val;
+} MFTtoProp;
+
+static MFTtoProp ft_to_prop[] =
+ { { "italic", 0, MFONT_STYLE, "i" },
+ { "roman", 0, MFONT_STYLE, "r" },
+ { "oblique", 0, MFONT_STYLE, "o" },
+ { "regular", 0, MFONT_WEIGHT, "normal" },
+ { "normal", 0, MFONT_WEIGHT, "normal" },
+ /* We need this entry even if "bold" is in commone_weight[] to
+ handle such style names as "bolditalic" and "boldoblique". */
+ { "bold", 0, MFONT_WEIGHT, "bold" },
+ { "demi bold", 0, MFONT_WEIGHT, "demibold" },
+ { "demi", 0, MFONT_WEIGHT, "demibold" } };
+static int ft_to_prop_size = sizeof ft_to_prop / sizeof ft_to_prop[0];
+
+/** List of FreeType fonts. Keys are family names, values are plists
+ containing fonts of the corresponding family. In the deeper
+ plist, keys are file names, values are (MFontFT *). */
+static MPlist *ft_font_list;
+
+/** List of FreeType fonts. Keys are script names, values are plists
+ containing fonts supporting the corresponding script. In the
+ deeper plist, keys are family names, values are (MFontFT *). */
+static MPlist *ft_script_list;
+
+/** List of FreeType fonts. Keys are language names, values are
+ plists containing fonts supporting the corresponding language. In
+ the deeper plist, keys are family names, values are (MFontFT *). */
+static MPlist *ft_language_list;
+
+static MPlist *ft_file_list;
+
+static int all_fonts_scaned;
+
+#define STRDUP_LOWER(s1, size, s2) \
+ do { \
+ int len = strlen (s2) + 1; \
+ char *p1, *p2; \
+ \
+ if ((size) < len) \
+ (s1) = alloca (len), (size) = len; \
+ for (p1 = (s1), p2 = (s2); *p2; p1++, p2++) \
+ *p1 = (*p2 >= 'A' && *p2 <= 'Z' ? *p2 + 'a' - 'A' : *p2); \
+ *p1 = '\0'; \
+ } while (0)
+
+
+static MPlist *ft_list_family (MSymbol, int);
+
+static void
+free_ft_rfont (void *object)
+{
+ MRealizedFontFT *ft_rfont = object;
+
+ if (! ft_rfont->face_encapsulated)
+ {
+ M17N_OBJECT_UNREF (ft_rfont->charmap_list);
+ FT_Done_Face (ft_rfont->ft_face);
+ }
+ free (ft_rfont);
+}
+
+static void
+free_ft_info (MFontFT *ft_info)
+{
+#ifdef HAVE_OTF
+ if (ft_info->otf && ft_info->otf != invalid_otf)
+ OTF_close (ft_info->otf);
+#endif /* HAVE_OTF */
+#ifdef HAVE_FONTCONFIG
+ if (ft_info->langset)
+ FcLangSetDestroy (ft_info->langset);
+ if (ft_info->charset)
+ FcCharSetDestroy (ft_info->charset);
+#endif /* HAVE_FONTCONFIG */
+ free (ft_info);
+}
+
+static MPlist *
+ft_get_charmaps (FT_Face ft_face)
+{
+ MPlist *plist = mplist ();
+ int unicode_bmp = -1, unicode_full = -1;
+ int i;
+
+ mplist_add (plist, Mt, (void *) -1);
+ for (i = 0; i < ft_face->num_charmaps; i++)
+ {
+ MSymbol registry = Mnil;
+
+ if (ft_face->charmaps[i]->platform_id == 0)
+ {
+ if (ft_face->charmaps[i]->encoding_id <= 4)
+ registry = M0[ft_face->charmaps[i]->encoding_id], unicode_bmp = i;
+ if (ft_face->charmaps[i]->encoding_id == 4)
+ unicode_bmp = unicode_full = i;
+ }
+ else if (ft_face->charmaps[i]->platform_id == 3)
+ {
+ if (ft_face->charmaps[i]->encoding_id == 1)
+ registry = M3_1, unicode_bmp = i;
+ else if (ft_face->charmaps[i]->encoding_id == 10)
+ unicode_bmp = unicode_full = i;
+ }
+ else if (ft_face->charmaps[i]->platform_id == 1
+ && ft_face->charmaps[i]->encoding_id == 0)
+ {
+ registry = M1_0;
+ mplist_add (plist, Mapple_roman, (void *) i);
+ }
+ if (registry == Mnil)
+ {
+ char registry_buf[16];
+
+ sprintf (registry_buf, "%d-%d",
+ ft_face->charmaps[i]->platform_id,
+ ft_face->charmaps[i]->encoding_id);
+ registry = msymbol (registry_buf);
+ }
+ mplist_add (plist, registry, (void *) i);
+ }
+ if (unicode_full >= 0)
+ mplist_add (plist, Municode_full, (void *) unicode_full);
+ if (unicode_bmp >= 0)
+ {
+ int i;
+
+ mplist_add (plist, Municode_bmp, (void *) unicode_bmp);
+ FT_Set_Charmap (ft_face, ft_face->charmaps[unicode_bmp]);
+ for (i = 0x21; i < 0x7F && FT_Get_Char_Index (ft_face, i) > 0; i++);
+ if (i == 0x7F)
+ {
+ for (i = 0xC0; i < 0x100 && FT_Get_Char_Index (ft_face, i) > 0; i++);
+ if (i == 0x100)
+ mplist_add (plist, Miso8859_1, (void *) unicode_bmp);
+ }
+ }
+
+ return plist;
+}
+
+static MFontFT *
+ft_gen_font (FT_Face ft_face)
+{
+ MFontFT *ft_info;
+ MFont *font;
+ char *buf;
+ int bufsize = 0;
+ char *stylename;
+ MSymbol family;
+ int size;
+
+ if (FT_IS_SCALABLE (ft_face))
+ size = ft_face->size->metrics.y_ppem;
+ else if (ft_face->num_fixed_sizes == 0)
+ return NULL;
+ else
+ size = ft_face->available_sizes[0].height;
+
+ MSTRUCT_CALLOC (ft_info, MERROR_FONT_FT);
+ font = &ft_info->font;
+ STRDUP_LOWER (buf, bufsize, ft_face->family_name);
+ family = msymbol (buf);
+ mfont__set_property (font, MFONT_FAMILY, family);
+ mfont__set_property (font, MFONT_WEIGHT, Mmedium);
+ mfont__set_property (font, MFONT_STYLE, Mr);
+ mfont__set_property (font, MFONT_STRETCH, Mnormal);
+ mfont__set_property (font, MFONT_ADSTYLE, Mnull);
+ mfont__set_property (font, MFONT_REGISTRY, Municode_bmp);
+ font->size = size * 10;
+ font->type = MFONT_TYPE_OBJECT;
+ font->source = MFONT_SOURCE_FT;
+ font->file = NULL;
+
+ stylename = ft_face->style_name;
+ while (*stylename)
+ {
+ int i;
+
+ for (i = 0; i < ft_to_prop_size; i++)
+ if (! strncasecmp (ft_to_prop[i].ft_style, stylename,
+ ft_to_prop[i].len))
+ {
+ mfont__set_property (font, ft_to_prop[i].prop,
+ msymbol (ft_to_prop[i].val));
+ stylename += ft_to_prop[i].len;
+ break;
+ }
+ if (i == ft_to_prop_size)
+ {
+ char *p1 = stylename + 1;
+ MSymbol sym;
+
+ while (*p1 >= 'a' && *p1 <= 'z') p1++;
+ sym = msymbol__with_len (stylename, p1 - stylename);
+ for (i = MFONT_WEIGHT; i <= MFONT_STRETCH; i++)
+ if (msymbol_get (sym, mfont__property_table[i].property))
+ {
+ mfont__set_property (font, i, sym);
+ break;
+ }
+ stylename = p1;
+ }
+ while (*stylename && ! isalpha (*stylename))
+ stylename++;
+ }
+ return ft_info;
+}
+
+#ifdef HAVE_FONTCONFIG
+
+typedef struct
+{
+ int fc_value;
+ char *m17n_value;
+ MSymbol sym;
+} FC_vs_M17N_font_prop;
+
+static FC_vs_M17N_font_prop fc_weight_table[] =
+ { { FC_WEIGHT_THIN, "thin" },
+ { FC_WEIGHT_ULTRALIGHT, "extralight" },
+ { FC_WEIGHT_LIGHT, "light" },
+#ifdef FC_WEIGHT_BOOK
+ { FC_WEIGHT_BOOK, "book" },
+#endif /* FC_WEIGHT_BOOK */
+ { FC_WEIGHT_REGULAR, "normal" },
+ { FC_WEIGHT_NORMAL, "normal" },
+ { FC_WEIGHT_MEDIUM, "medium" },
+ { FC_WEIGHT_DEMIBOLD, "demibold" },
+ { FC_WEIGHT_BOLD, "bold" },
+ { FC_WEIGHT_EXTRABOLD, "extrabold" },
+ { FC_WEIGHT_BLACK, "black" },
+ { FC_WEIGHT_HEAVY, "heavy" },
+ { FC_WEIGHT_MEDIUM, NULL } };
+int fc_weight_table_size =
+ sizeof fc_weight_table / sizeof (FC_vs_M17N_font_prop);
+
+static FC_vs_M17N_font_prop fc_slant_table[] =
+ { { FC_SLANT_ROMAN, "r" },
+ { FC_SLANT_ITALIC, "i" },
+ { FC_SLANT_OBLIQUE, "o" },
+ { FC_SLANT_ROMAN, NULL } };
+int fc_slant_table_size =
+ sizeof fc_slant_table / sizeof (FC_vs_M17N_font_prop);
+
+static FC_vs_M17N_font_prop fc_width_table[] =
+ { { FC_WIDTH_ULTRACONDENSED, "ultracondensed" },
+ { FC_WIDTH_EXTRACONDENSED, "extracondensed" },
+ { FC_WIDTH_CONDENSED, "condensed" },
+ { FC_WIDTH_SEMICONDENSED, "semicondensed" },
+ { FC_WIDTH_NORMAL, "normal" },
+ { FC_WIDTH_SEMIEXPANDED, "semiexpanded" },
+ { FC_WIDTH_EXPANDED, "expanded" },
+ { FC_WIDTH_EXTRAEXPANDED, "extraexpanded" },
+ { FC_WIDTH_ULTRAEXPANDED, "ultraexpanded" },
+ { FC_WIDTH_NORMAL, NULL } };
+int fc_width_table_size =
+ sizeof fc_width_table / sizeof (FC_vs_M17N_font_prop);
+
+
+static FC_vs_M17N_font_prop *fc_all_table[] =
+ { fc_weight_table, fc_slant_table, fc_width_table };
+
+static MSymbol
+fc_decode_prop (int val, FC_vs_M17N_font_prop *table, int size)
+{
+ int i = size / 2;
+
+ if (val < table[i].fc_value)
+ {
+ for (i--; i >= 0; i--)
+ if (val > table[i].fc_value)
+ break;
+ i++;
+ }
+ else
+ {
+ for (; i < size; i++)
+ if (val <= table[i].fc_value)
+ break;
+ }
+ return table[i].sym;
+}
+
+static int
+fc_encode_prop (MSymbol sym, FC_vs_M17N_font_prop *table)
+{
+ int i;
+
+ for (i = 0; table[i].m17n_value; i++)
+ if (table[i].sym == sym)
+ break;
+ return table[i].fc_value;
+}
+
+FcPattern *
+fc_get_pattern (MFont *font)
+{
+ FcPattern *pat = FcPatternCreate ();
+ MSymbol sym, weight, style, stretch;
+
+
+ if ((sym = (MSymbol) FONT_PROPERTY (font, MFONT_FOUNDRY)) != Mnil)
+ FcPatternAddString (pat, FC_FOUNDRY, (FcChar8 *) MSYMBOL_NAME (sym));
+ if ((sym = (MSymbol) FONT_PROPERTY (font, MFONT_FAMILY)) != Mnil)
+ FcPatternAddString (pat, FC_FAMILY, (FcChar8 *) MSYMBOL_NAME (sym));
+ if ((weight = (MSymbol) FONT_PROPERTY (font, MFONT_WEIGHT)) != Mnil)
+ FcPatternAddInteger (pat, FC_WEIGHT,
+ fc_encode_prop (weight, fc_weight_table));
+ if ((style = (MSymbol) FONT_PROPERTY (font, MFONT_STYLE)) != Mnil)
+ FcPatternAddInteger (pat, FC_SLANT,
+ fc_encode_prop (style, fc_slant_table));
+ if ((stretch = (MSymbol) FONT_PROPERTY (font, MFONT_STRETCH)) != Mnil)
+ FcPatternAddInteger (pat, FC_WIDTH,
+ fc_encode_prop (stretch, fc_width_table));
+ if (font->size > 0)
+ {
+ double size = font->size;
+ FcPatternAddDouble (pat, FC_PIXEL_SIZE, size / 10);
+ }
+ else if (font->size < 0)
+ {
+ double size = - font->size;
+ FcPatternAddDouble (pat, FC_SIZE, size / 10);
+ }
+ return pat;
+}
+
+static void
+fc_parse_pattern (FcPattern *pat, char *family, MFontFT *ft_info)
+{
+ FcChar8 *str;
+ int val;
+ double size;
+ char *buf;
+ int bufsize = 0;
+ MSymbol sym;
+ FcLangSet *ls;
+ FcCharSet *cs;
+ MFont *font = &ft_info->font;
+
+ MFONT_INIT (font);
+ if (FcPatternGetString (pat, FC_FOUNDRY, 0, &str) == FcResultMatch)
+ {
+ STRDUP_LOWER (buf, bufsize, (char *) str);
+ mfont__set_property (font, MFONT_FOUNDRY, msymbol (buf));
+ }
+ if (family)
+ mfont__set_property (font, MFONT_FAMILY, msymbol (family));
+ else if (FcPatternGetString (pat, FC_FAMILY, 0, &str) == FcResultMatch)
+ {
+ STRDUP_LOWER (buf, bufsize, (char *) str);
+ mfont__set_property (font, MFONT_FAMILY, msymbol (buf));
+ }
+ if (FcPatternGetInteger (pat, FC_WEIGHT, 0, &val) == FcResultMatch)
+ {
+ sym = fc_decode_prop (val, fc_weight_table, fc_weight_table_size);
+ mfont__set_property (font, MFONT_WEIGHT, sym);
+ }
+ if (FcPatternGetInteger (pat, FC_SLANT, 0, &val) == FcResultMatch)
+ {
+ sym = fc_decode_prop (val, fc_slant_table, fc_slant_table_size);
+ mfont__set_property (font, MFONT_STYLE, sym);
+ }
+ if (FcPatternGetInteger (pat, FC_WIDTH, 0, &val) == FcResultMatch)
+ {
+ sym = fc_decode_prop (val, fc_width_table, fc_width_table_size);
+ mfont__set_property (font, MFONT_STRETCH, sym);
+ }
+ if (FcPatternGetLangSet (pat, FC_LANG, 0, &ls) == FcResultMatch)
+ {
+ if (FcLangSetHasLang (ls, (FcChar8 *) "ja") != FcLangDifferentLang
+ || FcLangSetHasLang (ls, (FcChar8 *) "zh") != FcLangDifferentLang
+ || FcLangSetHasLang (ls, (FcChar8 *) "ko") != FcLangDifferentLang)
+ font->for_full_width = 1;
+ ft_info->langset = FcLangSetCopy (ls);
+ }
+ if (FcPatternGetCharSet (pat, FC_CHARSET, 0, &cs) == FcResultMatch)
+ ft_info->charset = FcCharSetCopy (cs);
+
+ mfont__set_property (font, MFONT_REGISTRY, Municode_bmp);
+ font->type = MFONT_TYPE_SPEC;
+ font->source = MFONT_SOURCE_FT;
+ if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
+ font->size = size * 10;
+ if (FcPatternGetString (pat, FC_FILE, 0, &str) == FcResultMatch)
+ font->file = msymbol ((char *) str);
+}
+
+
+static MFontFT *
+fc_gen_font (FcPattern *pat, char *family)
+{
+ MFontFT *ft_info;
+
+ MSTRUCT_CALLOC (ft_info, MERROR_FONT_FT);
+ fc_parse_pattern (pat, family, ft_info);
+ ft_info->font.type = MFONT_TYPE_OBJECT;
+ return ft_info;
+}
+
+static void
+fc_init_font_list (void)
+{
+ FcPattern *pattern = FcPatternCreate ();
+ FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, NULL);
+ FcFontSet *fs = FcFontList (fc_config, pattern, os);
+ MPlist *plist = mplist ();
+ char *buf;
+ int bufsize = 0;
+ int i;
+
+ ft_font_list = plist;
+ for (i = 0; i < fs->nfont; i++)
+ {
+ char *fam;
+
+ if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
+ (FcChar8 **) &fam) != FcResultMatch)
+ continue;
+ STRDUP_LOWER (buf, bufsize, fam);
+ plist = mplist_add (plist, msymbol (buf), NULL);
+ }
+ FcFontSetDestroy (fs);
+ FcObjectSetDestroy (os);
+ FcPatternDestroy (pattern);
+}
+
+static MPlist *
+fc_list_pattern (FcPattern *pattern)
+{
+ FcObjectSet *os = NULL;
+ FcFontSet *fs = NULL;
+ MSymbol last_family = Mnil;
+ MPlist *plist = NULL, *pl = NULL;
+ char *buf;
+ int bufsize = 0;
+ int i;
+
+ if (! (os = FcObjectSetBuild (FC_FAMILY, FC_FILE, NULL)))
+ goto err;
+ if (! (fs = FcFontList (fc_config, pattern, os)))
+ goto err;
+
+ for (i = 0; i < fs->nfont; i++)
+ {
+ MSymbol family, file;
+ char *fam, *filename;
+ MFontFT *ft_info;
+
+ if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
+ (FcChar8 **) &fam) != FcResultMatch)
+ continue;
+ if (FcPatternGetString (fs->fonts[i], FC_FILE, 0,
+ (FcChar8 **) &filename) != FcResultMatch)
+ continue;
+ STRDUP_LOWER (buf, bufsize, fam);
+ family = msymbol (buf);
+ file = msymbol (filename);
+ if (family != last_family)
+ {
+ pl = MPLIST_PLIST (ft_list_family (family, 0));
+ last_family = family;
+ }
+ ft_info = mplist_get (pl, file);
+ if (ft_info)
+ {
+ if (! plist)
+ plist = mplist ();
+ mplist_add (plist, family, ft_info);
+ }
+ }
+
+ err:
+ if (fs) FcFontSetDestroy (fs);
+ if (os) FcObjectSetDestroy (os);
+ return plist;
+}
+
+/* Return FcCharSet object built from CHAR_LIST or MT. In the latter
+ case, it is assured that the M-text contains at least one
+ character. */
+
+static FcCharSet *
+fc_build_charset (MPlist *char_list, MText *mt)
+{
+ FcCharSet *cs = FcCharSetCreate ();
+
+ if (! cs)
+ return NULL;
+ if (char_list)
+ {
+ for (; ! MPLIST_TAIL_P (char_list); char_list = MPLIST_NEXT (char_list))
+ if (! FcCharSetAddChar (cs, (FcChar32) MPLIST_INTEGER (char_list)))
+ {
+ FcCharSetDestroy (cs);
+ return NULL;
+ }
+ }
+ else
+ {
+ int i;
+
+ 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;
+}