+#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. */