X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=src%2Ffont-ft.c;h=1c595346790a9146f9b7d0a976f91fcf193d9915;hb=d68a1ef5c7dc1dd24430bfb11dac9a9035c11428;hp=67b331e6c4d22795962ebd7ca7572689d226b36d;hpb=18d69fe550e5470c6e7a49745629f757b095efff;p=m17n%2Fm17n-lib.git diff --git a/src/font-ft.c b/src/font-ft.c index 67b331e..1c59534 100644 --- a/src/font-ft.c +++ b/src/font-ft.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU Lesser General Public License along with the m17n library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 02111-1307, USA. */ #include "config.h" @@ -36,25 +36,59 @@ #include "internal.h" #include "plist.h" #include "symbol.h" +#include "language.h" +#include "internal-flt.h" #include "internal-gui.h" #include "font.h" #include "face.h" #ifdef HAVE_FREETYPE -#include +#ifdef HAVE_FTBDF_H +#include FT_BDF_H +#endif + +static int mdebug_flag = MDEBUG_FONT; #ifdef HAVE_FONTCONFIG -#include +#include -int fontconfig_initialized = 0; -FcConfig *fc_config; -#endif /* not HAVE_FONTCONFIG */ +static FcConfig *fc_config; +static MSymbol Mgeneric_family; +#endif /* HAVE_FONTCONFIG */ -static MSymbol Municode_bmp, Municode_full, Miso10646_1, Miso8859_1; +/* Font properties; Mnormal is already defined in face.c. */ +static MSymbol Mmedium, Mr, Mnull; + +static MSymbol M0[5], M3_1, M1_0; static FT_Library ft_library; +#ifdef HAVE_OTF +static OTF *invalid_otf = (OTF *) ""; +#endif /* HAVE_OTF */ + +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; @@ -66,9 +100,9 @@ typedef struct static MFTtoProp ft_to_prop[] = { { "italic", 0, MFONT_STYLE, "i" }, { "roman", 0, MFONT_STYLE, "r" }, - { "oblique", 0, MFONT_STYLE, "p" }, - { "regular", 0, MFONT_WEIGHT, "medium" }, - { "normal", 0, MFONT_WEIGHT, "medium" }, + { "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" }, @@ -77,494 +111,1463 @@ static MFTtoProp ft_to_prop[] = 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 - contains fonts of the corresponding family. In the deeper plist, - keys are Mt, values are (MFTInfo *). */ + 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; -/** Return 0 if NAME implies TrueType or OpenType fonts. Othersize - return -1. */ +#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 int -check_otf_filename (const char *name) -{ - int len = strlen (name); - const char *ext = name + (len - 4); - - if (len < 5 - || (memcmp (ext, ".ttf", 4) - && memcmp (ext, ".TTF", 4) - && memcmp (ext, ".otf", 4) - && memcmp (ext, ".OTF", 4))) - return -1; - return 0; -} -/** Setup members of FT_INFO from FT_FACE. Return the family name. */ +static MPlist *ft_list_family (MSymbol, int); -static MSymbol -set_font_info (FT_Face ft_face, MFTInfo *ft_info, MSymbol family) +static void +free_ft_rfont (void *object) { - MFont *font = &ft_info->font; - int len; - char *buf, *p; - MPlist *charmap_list; - int unicode_bmp = -1, unicode_full = -1, unicode = -1; - int i; - - MFONT_INIT (font); + MRealizedFontFT *ft_rfont = object; - if (family == Mnil) + if (! ft_rfont->face_encapsulated) { - len = strlen (ft_face->family_name) + 1; - buf = (char *) alloca (len); - memcpy (buf, ft_face->family_name, len); - for (p = buf; *p; p++) - if (*p >= 'A' && *p <= 'Z') - *p += 'a' - 'A'; - family = msymbol (buf); + M17N_OBJECT_UNREF (ft_rfont->charmap_list); + FT_Done_Face (ft_rfont->ft_face); } - mfont__set_property (font, MFONT_FAMILY, family); - mfont__set_property (font, MFONT_WEIGHT, msymbol ("medium")); - mfont__set_property (font, MFONT_STYLE, msymbol ("r")); - mfont__set_property (font, MFONT_STRETCH, msymbol ("normal")); - mfont__set_property (font, MFONT_ADSTYLE, msymbol ("")); - - if (ft_face->style_name) - { - len = strlen (ft_face->style_name) + 1; - buf = (char *) alloca (len); - memcpy (buf, ft_face->style_name, len); - for (p = buf; *p; p++) - if (*p >= 'A' && *p <= 'Z') - *p += 'a' - 'A'; - p = buf; - while (*p && (*p < 'a' || *p > 'z')) p++; - while (*p) - { - for (i = 0; i < ft_to_prop_size; i++) - if (! strncmp (ft_to_prop[i].ft_style, p, ft_to_prop[i].len)) - { - mfont__set_property (font, ft_to_prop[i].prop, - msymbol (ft_to_prop[i].val)); - p += ft_to_prop[i].len; - break; - } - if (i == ft_to_prop_size) - { - char *p1 = p + 1; - MSymbol sym; + free (ft_rfont); +} - while (*p1 >= 'a' && *p1 <= 'z') p1++; - sym = msymbol__with_len (p, p1 - p); - for (i = MFONT_WEIGHT; i <= MFONT_STYLE; i++) - if (msymbol_get (sym, mfont__property_table[i].property)) - { - mfont__set_property (font, i, sym); - break; - } - p = p1; - } - while (*p && (*p < 'a' || *p > 'z')) p++; - } - } +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; - charmap_list = mplist (); - mplist_add (charmap_list, Mt, (void *) -1); + mplist_add (plist, Mt, (void *) -1); for (i = 0; i < ft_face->num_charmaps; i++) { - char registry_buf[16]; - MSymbol registry; + MSymbol registry = Mnil; - sprintf (registry_buf, "%d-%d", - ft_face->charmaps[i]->platform_id, - ft_face->charmaps[i]->encoding_id); - registry = msymbol (registry_buf); - mplist_add (charmap_list, registry, (void *) i); - if (ft_face->charmaps[i]->platform_id == 0) { - if (ft_face->charmaps[i]->encoding_id == 3) - unicode_bmp = i; - else if (ft_face->charmaps[i]->encoding_id == 4) - unicode_full = i; + 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) - unicode_bmp = i; + registry = M3_1, unicode_bmp = i; else if (ft_face->charmaps[i]->encoding_id == 10) - unicode_full = i; + unicode_bmp = unicode_full = i; } else if (ft_face->charmaps[i]->platform_id == 1 && ft_face->charmaps[i]->encoding_id == 0) - mplist_add (charmap_list, msymbol ("apple-roman"), (void *) i); + { + 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) { - mplist_add (charmap_list, Municode_full, (void *) unicode_full); - mplist_add (charmap_list, Municode_bmp, (void *) unicode_full); - mplist_add (charmap_list, Miso10646_1, (void *) unicode_full); - unicode = unicode_full; - } - else if (unicode_bmp >= 0) - { - mplist_add (charmap_list, Municode_bmp, (void *) unicode_bmp); - mplist_add (charmap_list, Miso10646_1, (void *) unicode_bmp); - unicode = unicode_bmp; + 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); + } } - if (unicode >= 0) + + 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) { - FT_Set_Charmap (ft_face, ft_face->charmaps[unicode]); - for (i = 255; i >= 32; i--) - { - if (i == 160) - i = 126; - if (FT_Get_Char_Index (ft_face, (FT_ULong) i) == 0) + 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; } - if (i == 31) - mplist_add (charmap_list, Miso8859_1, (void *) unicode); + 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); - ft_info->charmap_list = charmap_list; +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 (! FT_IS_SCALABLE (ft_face)) + if (val < table[i].fc_value) { - BDF_PropertyRec prop; - - FT_Get_BDF_Property (ft_face, "PIXEL_SIZE", &prop); - font->property[MFONT_SIZE] = prop.u.integer * 10; - FT_Get_BDF_Property (ft_face, "RESOLUTION_Y", &prop); - font->property[MFONT_RESY] = prop.u.integer; + for (i--; i >= 0; i--) + if (val > table[i].fc_value) + break; + i++; } - - return family; + 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; +} -static void -close_ft (void *object) +FcPattern * +fc_get_pattern (MFont *font) { - MFTInfo *ft_info = object; + FcPattern *pat = FcPatternCreate (); + MSymbol sym, weight, style, stretch; + - if (ft_info->ft_face) + 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) { - if (ft_info->extra_info) - M17N_OBJECT_UNREF (ft_info->extra_info); - FT_Done_Face (ft_info->ft_face); -#ifdef HAVE_OTF - if (ft_info->otf) - OTF_close (ft_info->otf); -#endif /* HAVE_OTF */ + double size = font->size; + FcPatternAddDouble (pat, FC_PIXEL_SIZE, size / 10); } - free (ft_info->filename); - if (ft_info->languages) - free (ft_info->languages); - M17N_OBJECT_UNREF (ft_info->charmap_list); - free (ft_info); + else if (font->size < 0) + { + double size = - font->size; + FcPatternAddDouble (pat, FC_SIZE, size / 10); + } + return pat; } static void -add_font_info (char *filename, MSymbol family, char *languages) +fc_parse_pattern (FcPattern *pat, char *family, MFontFT *ft_info) { - FT_Face ft_face; - BDF_PropertyRec prop; - MFTInfo *ft_info = NULL; + FcChar8 *str; + int val; + double size; + char *buf; + int bufsize = 0; + MSymbol sym; + FcLangSet *ls; + FcCharSet *cs; + MFont *font = &ft_info->font; - if (FT_New_Face (ft_library, filename, 0, &ft_face) == 0) + MFONT_INIT (font); + if (FcPatternGetString (pat, FC_FOUNDRY, 0, &str) == FcResultMatch) { - if (FT_IS_SCALABLE (ft_face) - || FT_Get_BDF_Property (ft_face, "PIXEL_SIZE", &prop) == 0) - { - MSymbol fam; - MPlist *plist; - - M17N_OBJECT (ft_info, close_ft, MERROR_FONT_FT); - ft_info->filename = strdup (filename); - if (languages) - ft_info->languages = strdup (languages); - ft_info->otf_flag = check_otf_filename (filename); - fam = set_font_info (ft_face, ft_info, family); - plist = mplist_get (ft_font_list, fam); - if (! plist) - { - plist = mplist (); - mplist_add (ft_font_list, fam, plist); - } - mplist_add (plist, fam, ft_info); - } - FT_Done_Face (ft_face); + 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); } -#ifdef HAVE_FONTCONFIG -static MPlist *fc_generic_family_list; +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_list (MSymbol family) +fc_init_font_list (void) { - FcPattern *pattern; - FcObjectSet *os; - FcFontSet *fs; + 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; - if (! fc_config) + ft_font_list = plist; + for (i = 0; i < fs->nfont; i++) { - char *pathname; - struct stat buf; - MPlist *plist; + char *fam; - FcInit (); - fc_config = FcConfigGetCurrent (); - MPLIST_DO (plist, mfont_freetype_path) - if (MPLIST_STRING_P (plist) - && (pathname = MPLIST_STRING (plist)) - && stat (pathname, &buf) == 0) - FcConfigAppFontAddDir (fc_config, (FcChar8 *) pathname); - /* TODO: avoid duplicate addition by checking the current font - directory list given by FcConfigGetFontDirs (). */ + 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; - pattern = FcPatternCreate (); - if (family) - FcPatternAddString (pattern, FC_FAMILY, - (FcChar8 *) (msymbol_name (family))); - os = FcObjectSetBuild (FC_FILE, FC_FAMILY, FC_LANG, NULL); - fs = FcFontList (fc_config, pattern, os); - if (fs->nfont == 0 - && family != Mnil - && mplist_get (fc_generic_family_list, family) - && FcConfigSubstitute (fc_config, pattern, FcMatchPattern) == FcTrue) - { - FcFontSetDestroy (fs); - fs = FcFontList (fc_config, pattern, os); - } for (i = 0; i < fs->nfont; i++) { - char *filename, *languages; + MSymbol family, file; + char *fam, *filename; + MFontFT *ft_info; - FcPatternGetString (fs->fonts[i], FC_FILE, 0, (FcChar8 **) &filename); - FcPatternGetString (fs->fonts[i], FC_LANG, 0, (FcChar8 **) &languages); - if (family == Mnil) + 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) { - MSymbol fam; - char *fname; - - FcPatternGetString (fs->fonts[i], FC_FAMILY, 0, (FcChar8 **) &fname); - fam = msymbol (fname); - if (! mplist_get (ft_font_list, fam)) - add_font_info (filename, fam, languages); + 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); } - else - add_font_info (filename, family, languages); } - FcFontSetDestroy (fs); - FcObjectSetDestroy (os); - FcPatternDestroy (pattern); + + err: + if (fs) FcFontSetDestroy (fs); + if (os) FcObjectSetDestroy (os); + return plist; } -#else /* not HAVE_FONTCONFIG */ +/* 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 void -ft_list_all () +static FcCharSet * +fc_build_charset (MPlist *char_list, MText *mt) { - MPlist *plist; - struct stat buf; - char *pathname; + FcCharSet *cs = FcCharSetCreate (); - 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)) - add_font_info (pathname, Mnil, NULL); - else if (S_ISDIR (buf.st_mode)) + 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))) { - int len = strlen (pathname); - char path[PATH_MAX]; - DIR *dir = opendir (pathname); - struct dirent *dp; + FcCharSetDestroy (cs); + return NULL; + } + } + else + { + int i; - if (dir) + 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)) { - strcpy (path, pathname); - strcpy (path + len, "/"); - len++; - while ((dp = readdir (dir)) != NULL) + MFontFT *ft_info = MPLIST_VAL (pl); + + if (ft_info->font.file == filename) { - strcpy (path + len, dp->d_name); - add_font_info (path, Mnil, NULL); + plist = mplist (); + mplist_add (plist, family, ft_info); + break; } - closedir (dir); } } } -} + } +#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 */ + mplist_push (ft_file_list, filename, plist); + return plist; +} + /* The FreeType font driver function SELECT. */ -static MRealizedFont * -ft_select (MFrame *frame, MFont *spec, MFont *request, int limited_size) +static MFont * +ft_select (MFrame *frame, MFont *font, int limited_size) { + MFont *found = NULL; +#ifdef HAVE_FONTCONFIG MPlist *plist, *pl; - MFTInfo *best_font; - int best_score; - MRealizedFont *rfont; - MSymbol family, registry; + MFontFT *ft_info; + int check_font_property = 1; - best_font = NULL; - best_score = 0; - family = FONT_PROPERTY (spec, MFONT_FAMILY); - if (family == Mnil) - family = FONT_PROPERTY (request, MFONT_FAMILY); - registry = FONT_PROPERTY (spec, MFONT_REGISTRY); - if (registry == Mnil) - registry = Mt; - - if (! ft_font_list) - ft_font_list = mplist (); -#ifdef HAVE_FONTCONFIG - if (family != Mnil) + if (font->file != Mnil) { - plist = mplist_get (ft_font_list, family); + plist = ft_list_file (font->file); if (! plist) - { - fc_list (family); - plist = mplist_get (ft_font_list, family); - if (! plist) - { - mplist_add (ft_font_list, family, plist = mplist ()); - return NULL; - } - } + return NULL; + check_font_property = 0; } else { - if (! all_fonts_scaned) - { - fc_list (Mnil); - all_fonts_scaned = 1; - } - } -#else /* not HAVE_FONTCONFIG */ - if (! all_fonts_scaned) - { - ft_list_all (); - all_fonts_scaned = 1; - } - if (family != Mnil) - { - plist = mplist_get (ft_font_list, family); - if (! plist) + MSymbol family = FONT_PROPERTY (font, MFONT_FAMILY); + + if (family) + plist = MPLIST_PLIST (ft_list_family (family, 1)); + else + plist = ft_list_default (); + if (MPLIST_TAIL_P (plist)) return NULL; } -#endif /* not HAVE_FONTCONFIG */ - if (family == Mnil) - plist = MPLIST_VAL (ft_font_list); + plist = mplist_copy (plist); - retry: - MPLIST_DO (pl, plist) + if (font->capability != Mnil) { - MFTInfo *ft_info = MPLIST_VAL (pl); - int score; + MFontCapability *cap = mfont__get_capability (font->capability); - if (! mplist_find_by_key (ft_info->charmap_list, registry)) - continue; - /* We always ignore FOUNDRY. */ - ft_info->font.property[MFONT_FOUNDRY] = spec->property[MFONT_FOUNDRY]; - score = mfont__score (&ft_info->font, spec, request, limited_size); - if (score >= 0 - && (! best_font - || best_score > score)) + for (pl = plist; ! MPLIST_TAIL_P (pl);) { - best_font = ft_info; - best_score = score; - if (score == 0) - break; + if (cap->script_tag && ft_check_cap_otf (MPLIST_VAL (pl), cap, NULL) < 0) + { + mplist_pop (pl); + continue; + } + if (cap->language + && ft_check_language (MPLIST_VAL (pl), cap->language, NULL) < 0) + mplist_pop (pl); + else + pl = MPLIST_NEXT (pl); } } - if (family == Mnil) + + if (check_font_property) { - plist = MPLIST_NEXT (plist); - if (! MPLIST_TAIL_P (plist)) - goto retry; + MSymbol weight = FONT_PROPERTY (font, MFONT_WEIGHT); + MSymbol style = FONT_PROPERTY (font, MFONT_STYLE); + MSymbol stretch = FONT_PROPERTY (font, MFONT_STRETCH); + MSymbol alternate_weight = Mnil; + + if (weight == Mnormal) + alternate_weight = Mmedium; + else if (weight == Mmedium) + alternate_weight = Mnormal; + if (weight != Mnil || style != Mnil || stretch != Mnil || font->size > 0) + for (pl = plist; ! MPLIST_TAIL_P (pl); ) + { + ft_info = MPLIST_VAL (pl); + if ((weight != Mnil + && (weight != FONT_PROPERTY (&ft_info->font, MFONT_WEIGHT) + && alternate_weight != FONT_PROPERTY (&ft_info->font, MFONT_WEIGHT))) + || (style != Mnil + && style != FONT_PROPERTY (&ft_info->font, MFONT_STYLE)) + || (stretch != Mnil + && stretch != FONT_PROPERTY (&ft_info->font, + MFONT_STRETCH)) + || (font->size > 0 + && ft_info->font.size > 0 + && ft_info->font.size != font->size)) + mplist_pop (pl); + else + pl = MPLIST_NEXT (pl); + } } - if (! best_font) - return NULL; - MSTRUCT_CALLOC (rfont, MERROR_FONT_FT); - rfont->frame = frame; - rfont->spec = *spec; - rfont->request = *request; - rfont->font = best_font->font; - rfont->font.property[MFONT_SIZE] = request->property[MFONT_SIZE]; - rfont->font.property[MFONT_REGISTRY] = spec->property[MFONT_REGISTRY]; - rfont->score = best_score; - rfont->info = best_font; - M17N_OBJECT_REF (best_font); - return rfont; + MPLIST_DO (pl, plist) + { + font = MPLIST_VAL (plist); + if (limited_size == 0 + || font->size == 0 + || font->size <= limited_size) + { + found = font; + break; + } + } + M17N_OBJECT_UNREF (plist); +#endif /* HAVE_FONTCONFIG */ + return found; } -/* The FreeType font driver function OPEN. */ - -static int -ft_open (MRealizedFont *rfont) +static MRealizedFont * +ft_open (MFrame *frame, MFont *font, MFont *spec, MRealizedFont *rfont) { - MFTInfo *base = rfont->info, *ft_info; - MSymbol registry = FONT_PROPERTY (&rfont->font, MFONT_REGISTRY); - int mdebug_mask = MDEBUG_FONT; + MFontFT *ft_info = (MFontFT *) font; + int reg = spec->property[MFONT_REGISTRY]; + MSymbol registry = FONT_PROPERTY (spec, MFONT_REGISTRY); + MRealizedFontFT *ft_rfont; + FT_Face ft_face; + MPlist *plist, *charmap_list = NULL; + int charmap_index; int size; - M17N_OBJECT (ft_info, close_ft, MERROR_FONT_FT); - ft_info->font = base->font; - ft_info->filename = strdup (base->filename); - ft_info->otf_flag = base->otf_flag; - ft_info->charmap_list = base->charmap_list; - M17N_OBJECT_REF (ft_info->charmap_list); - M17N_OBJECT_UNREF (base); - rfont->info = ft_info; - - rfont->status = -1; - ft_info->ft_face = NULL; - if (FT_New_Face (ft_library, ft_info->filename, 0, &ft_info->ft_face)) - goto err; - if (registry == Mnil) - registry = Mt; - ft_info->charmap_index - = (int) mplist_get (((MFTInfo *) rfont->info)->charmap_list, registry); - if (ft_info->charmap_index >= 0 - && FT_Set_Charmap (ft_info->ft_face, - ft_info->ft_face->charmaps[ft_info->charmap_index])) - goto err; - size = rfont->font.property[MFONT_SIZE] / 10; - if (FT_Set_Pixel_Sizes (ft_info->ft_face, 0, size)) - goto err; + if (font->size) + /* non-scalable font */ + size = font->size; + else if (spec->size) + { + int ratio = mfont_resize_ratio (font); - MDEBUG_PRINT1 (" [FT-FONT] o %s\n", ft_info->filename); - rfont->status = 1; - rfont->ascent = ft_info->ft_face->ascender >> 6; - rfont->descent = - (ft_info->ft_face->descender >> 6); - rfont->type = Mfreetype; - rfont->fontp = ft_info->ft_face; - return 0; + size = ratio == 100 ? spec->size : spec->size * ratio / 100; + } + else + size = 120; - err: - MDEBUG_PRINT1 (" [FT-FONT] x %s\n", ft_info->filename); - if (ft_info->ft_face) - FT_Done_Face (ft_info->ft_face); - M17N_OBJECT_UNREF (ft_info->charmap_list); - free (ft_info->filename); - free (ft_info); - rfont->info = NULL; - return -1; + if (rfont) + { + charmap_list = ((MRealizedFontFT *) rfont->info)->charmap_list; + for (; rfont; rfont = rfont->next) + if (rfont->font == font + && (rfont->font->size ? rfont->font->size == size + : rfont->spec.size == size) + && rfont->spec.property[MFONT_REGISTRY] == reg + && rfont->driver == &mfont__ft_driver) + return rfont; + } + + MDEBUG_DUMP (" [FONT-FT] opening ", "", mdebug_dump_font (&ft_info->font)); + + if (FT_New_Face (ft_library, MSYMBOL_NAME (ft_info->font.file), 0, + &ft_face)) + { + font->type = MFONT_TYPE_FAILURE; + MDEBUG_PRINT (" no (FT_New_Face)\n"); + return NULL; + } + if (charmap_list) + M17N_OBJECT_REF (charmap_list); + else + charmap_list = ft_get_charmaps (ft_face); + if (registry == Mnil) + registry = Municode_bmp; + plist = mplist_find_by_key (charmap_list, registry); + if (! plist) + { + FT_Done_Face (ft_face); + M17N_OBJECT_UNREF (charmap_list); + MDEBUG_PRINT1 (" no (%s)\n", MSYMBOL_NAME (registry)); + return NULL; + } + charmap_index = (int) MPLIST_VAL (plist); + if ((charmap_index >= 0 + && FT_Set_Charmap (ft_face, ft_face->charmaps[charmap_index])) + || FT_Set_Pixel_Sizes (ft_face, 0, size / 10)) + { + FT_Done_Face (ft_face); + M17N_OBJECT_UNREF (charmap_list); + font->type = MFONT_TYPE_FAILURE; + MDEBUG_PRINT1 (" no (size %d)\n", size); + return NULL; + } + + M17N_OBJECT (ft_rfont, free_ft_rfont, MERROR_FONT_FT); + ft_rfont->ft_face = ft_face; + ft_rfont->charmap_list = charmap_list; + MSTRUCT_CALLOC (rfont, MERROR_FONT_FT); + rfont->spec = *font; + rfont->spec.type = MFONT_TYPE_REALIZED; + rfont->spec.property[MFONT_REGISTRY] = reg; + rfont->spec.size = size; + rfont->frame = frame; + rfont->font = font; + rfont->driver = &mfont__ft_driver; + rfont->info = ft_rfont; + rfont->fontp = ft_face; + rfont->ascent = ft_face->size->metrics.ascender; + rfont->descent = - ft_face->size->metrics.descender; + rfont->max_advance = ft_face->size->metrics.max_advance; + rfont->baseline_offset = 0; + rfont->x_ppem = ft_face->size->metrics.x_ppem; + rfont->y_ppem = ft_face->size->metrics.y_ppem; +#ifdef HAVE_FTBDF_H + { + BDF_PropertyRec prop; + + if (! FT_IS_SCALABLE (ft_face) + && FT_Get_BDF_Property (ft_face, "_MULE_BASELINE_OFFSET", &prop) == 0) + { + rfont->baseline_offset = prop.u.integer << 6; + rfont->ascent += prop.u.integer << 6; + rfont->descent -= prop.u.integer << 6; + } + } +#endif /* HAVE_FTBDF_H */ + if (FT_IS_SCALABLE (ft_face)) + rfont->average_width = 0; + else + rfont->average_width = ft_face->available_sizes->width << 6; + rfont->next = MPLIST_VAL (frame->realized_font_list); + MPLIST_VAL (frame->realized_font_list) = rfont; + MDEBUG_PRINT (" ok\n"); + return rfont; } /* The FreeType font driver function FIND_METRIC. */ @@ -573,41 +1576,49 @@ static void ft_find_metric (MRealizedFont *rfont, MGlyphString *gstring, int from, int to) { - MFTInfo *ft_info = (MFTInfo *) rfont->info; - FT_Face ft_face = ft_info->ft_face; + FT_Face ft_face = rfont->fontp; MGlyph *g = MGLYPH (from), *gend = MGLYPH (to); for (; g != gend; g++) { - if (g->code == MCHAR_INVALID_CODE) + if (g->g.measured) + continue; + if (g->g.code == MCHAR_INVALID_CODE) { if (FT_IS_SCALABLE (ft_face)) { - unsigned unitsPerEm = ft_face->units_per_EM; - int size = rfont->font.property[MFONT_SIZE] / 10; - - g->lbearing = 0; - g->rbearing = ft_face->max_advance_width * size / unitsPerEm; - g->width = ft_face->max_advance_width * size / unitsPerEm; - g->ascent = ft_face->ascender * size / unitsPerEm; - g->descent = (- ft_face->descender) * size / unitsPerEm; + g->g.lbearing = 0; + g->g.rbearing = ft_face->size->metrics.max_advance; + g->g.xadv = g->g.rbearing; + g->g.ascent = ft_face->size->metrics.ascender; + g->g.descent = - ft_face->size->metrics.descender; } else { +#ifdef HAVE_FTBDF_H BDF_PropertyRec prop; +#endif /* HAVE_FTBDF_H */ - g->lbearing = 0; - g->rbearing = g->width = ft_face->available_sizes->width; + g->g.lbearing = 0; + g->g.rbearing = g->g.xadv = ft_face->available_sizes->width << 6; +#ifdef HAVE_FTBDF_H if (FT_Get_BDF_Property (ft_face, "ASCENT", &prop) == 0) { - g->ascent = prop.u.integer; + g->g.ascent = prop.u.integer << 6; FT_Get_BDF_Property (ft_face, "DESCENT", &prop); - g->descent = prop.u.integer; + g->g.descent = prop.u.integer << 6; + if (FT_Get_BDF_Property (ft_face, "_MULE_BASELINE_OFFSET", + & prop) == 0) + { + g->g.ascent += prop.u.integer << 6; + g->g.descent -= prop.u.integer << 6; + } } else +#endif /* HAVE_FTBDF_H */ { - g->ascent = ft_face->available_sizes->height; - g->descent = 0; + g->g.ascent = ft_face->available_sizes->height << 6; + g->g.descent = 0; } } } @@ -615,35 +1626,105 @@ ft_find_metric (MRealizedFont *rfont, MGlyphString *gstring, { FT_Glyph_Metrics *metrics; - FT_Load_Glyph (ft_face, (FT_UInt) g->code, FT_LOAD_DEFAULT); + FT_Load_Glyph (ft_face, (FT_UInt) g->g.code, FT_LOAD_DEFAULT); metrics = &ft_face->glyph->metrics; - g->lbearing = (metrics->horiBearingX >> 6); - g->rbearing = (metrics->horiBearingX + metrics->width) >> 6; - g->width = metrics->horiAdvance >> 6; - g->ascent = metrics->horiBearingY >> 6; - g->descent = (metrics->height - metrics->horiBearingY) >> 6; + g->g.lbearing = metrics->horiBearingX; + g->g.rbearing = metrics->horiBearingX + metrics->width; + g->g.xadv = metrics->horiAdvance; + g->g.ascent = metrics->horiBearingY; + g->g.descent = metrics->height - metrics->horiBearingY; + } + g->g.yadv = 0; + g->g.ascent += rfont->baseline_offset; + g->g.descent -= rfont->baseline_offset; + g->g.measured = 1; + } +} + +static int +ft_has_char (MFrame *frame, MFont *font, MFont *spec, int c, unsigned code) +{ + MRealizedFont *rfont = NULL; + MRealizedFontFT *ft_rfont; + FT_UInt idx; + + if (font->type == MFONT_TYPE_REALIZED) + rfont = (MRealizedFont *) font; + else if (font->type == MFONT_TYPE_OBJECT) + { + for (rfont = MPLIST_VAL (frame->realized_font_list); rfont; + rfont = rfont->next) + if (rfont->font == font && rfont->driver == &mfont__ft_driver) + break; + if (! rfont) + { +#ifdef HAVE_FONTCONFIG + MFontFT *ft_info = (MFontFT *) font; + + if (! ft_info->charset) + { + FcPattern *pat = FcPatternBuild (NULL, FC_FILE, FcTypeString, + MSYMBOL_NAME (font->file), + NULL); + FcObjectSet *os = FcObjectSetBuild (FC_CHARSET, NULL); + FcFontSet *fs = FcFontList (fc_config, pat, os); + + if (fs->nfont > 0 + && FcPatternGetCharSet (fs->fonts[0], FC_CHARSET, 0, + &ft_info->charset) == FcResultMatch) + ft_info->charset = FcCharSetCopy (ft_info->charset); + else + ft_info->charset = FcCharSetCreate (); + FcFontSetDestroy (fs); + FcObjectSetDestroy (os); + FcPatternDestroy (pat); + } + return (FcCharSetHasChar (ft_info->charset, (FcChar32) c) == FcTrue); +#else /* not HAVE_FONTCONFIG */ + rfont = ft_open (frame, font, spec, NULL); +#endif /* not HAVE_FONTCONFIG */ } } + else + MFATAL (MERROR_FONT_FT); + + if (! rfont) + return 0; + ft_rfont = rfont->info; + idx = FT_Get_Char_Index (ft_rfont->ft_face, (FT_ULong) code); + return (idx != 0); } /* The FreeType font driver function ENCODE_CHAR. */ static unsigned -ft_encode_char (MRealizedFont *rfont, unsigned code) +ft_encode_char (MFrame *frame, MFont *font, MFont *spec, unsigned code) { - MFTInfo *ft_info; + MRealizedFont *rfont; + MRealizedFontFT *ft_rfont; + FT_UInt idx; - if (rfont->status == 0) + if (font->type == MFONT_TYPE_REALIZED) + rfont = (MRealizedFont *) font; + else if (font->type == MFONT_TYPE_OBJECT) { - if ((rfont->driver->open) (rfont) < 0) - return -1; + for (rfont = MPLIST_VAL (frame->realized_font_list); rfont; + rfont = rfont->next) + if (rfont->font == font && rfont->driver == &mfont__ft_driver) + break; + if (! rfont) + { + rfont = ft_open (frame, font, spec, NULL); + if (! rfont) + return -1; + } } - ft_info = (MFTInfo *) rfont->info; - code = (unsigned) FT_Get_Char_Index (ft_info->ft_face, (FT_ULong) code); - if (! code) - return MCHAR_INVALID_CODE; - return (code); + else + MFATAL (MERROR_FONT_FT); + ft_rfont = rfont->info; + idx = FT_Get_Char_Index (ft_rfont->ft_face, (FT_ULong) code); + return (idx ? (unsigned) idx : MCHAR_INVALID_CODE); } /* The FreeType font driver function RENDER. */ @@ -660,7 +1741,6 @@ ft_render (MDrawWindow win, int x, int y, MGlyphString *gstring, MGlyph *from, MGlyph *to, int reverse, MDrawRegion region) { - MFTInfo *ft_info; FT_Face ft_face; MRealizedFace *rface = from->rface; MFrame *frame = rface->frame; @@ -668,14 +1748,16 @@ ft_render (MDrawWindow win, int x, int y, MGlyph *g; int i, j; MPointTable point_table[8]; + int baseline_offset; + int pixel_mode = -1; if (from == to) return; /* It is assured that the all glyphs in the current range use the same realized face. */ - ft_info = (MFTInfo *) rface->rfont->info; - ft_face = ft_info->ft_face; + ft_face = rface->rfont->fontp; + baseline_offset = rface->rfont->baseline_offset >> 6; if (! gstring->anti_alias) { @@ -689,7 +1771,7 @@ ft_render (MDrawWindow win, int x, int y, for (i = 0; i < 8; i++) point_table[i].p = point_table[i].points; - for (g = from; g < to; x += g++->width) + for (g = from; g < to; x += g++->g.xadv) { unsigned char *bmp; int intensity; @@ -697,21 +1779,19 @@ ft_render (MDrawWindow win, int x, int y, int xoff, yoff; int width, pitch; - FT_Load_Glyph (ft_face, (FT_UInt) g->code, load_flags); - yoff = y - ft_face->glyph->bitmap_top + g->yoff; + FT_Load_Glyph (ft_face, (FT_UInt) g->g.code, load_flags); + if (pixel_mode < 0) + pixel_mode = ft_face->glyph->bitmap.pixel_mode; + yoff = y - ft_face->glyph->bitmap_top + g->g.yoff; bmp = ft_face->glyph->bitmap.buffer; width = ft_face->glyph->bitmap.width; pitch = ft_face->glyph->bitmap.pitch; - if (! gstring->anti_alias) - pitch *= 8; - if (width > pitch) - width = pitch; - if (gstring->anti_alias) + if (pixel_mode != FT_PIXEL_MODE_MONO) for (i = 0; i < ft_face->glyph->bitmap.rows; i++, bmp += ft_face->glyph->bitmap.pitch, yoff++) { - xoff = x + ft_face->glyph->bitmap_left + g->xoff; + xoff = x + ft_face->glyph->bitmap_left + g->g.xoff; for (j = 0; j < width; j++, xoff++) { intensity = bmp[j] >> 5; @@ -719,7 +1799,7 @@ ft_render (MDrawWindow win, int x, int y, { ptable = point_table + intensity; ptable->p->x = xoff; - ptable->p->y = yoff; + ptable->p->y = yoff - baseline_offset; ptable->p++; if (ptable->p - ptable->points == NUM_POINTS) { @@ -736,7 +1816,7 @@ ft_render (MDrawWindow win, int x, int y, for (i = 0; i < ft_face->glyph->bitmap.rows; i++, bmp += ft_face->glyph->bitmap.pitch, yoff++) { - xoff = x + ft_face->glyph->bitmap_left + g->xoff; + xoff = x + ft_face->glyph->bitmap_left + g->g.xoff; for (j = 0; j < width; j++, xoff++) { intensity = bmp[j / 8] & (1 << (7 - (j % 8))); @@ -744,7 +1824,7 @@ ft_render (MDrawWindow win, int x, int y, { ptable = point_table; ptable->p->x = xoff; - ptable->p->y = yoff; + ptable->p->y = yoff - baseline_offset; ptable->p++; if (ptable->p - ptable->points == NUM_POINTS) { @@ -758,7 +1838,7 @@ ft_render (MDrawWindow win, int x, int y, } } - if (gstring->anti_alias) + if (pixel_mode != FT_PIXEL_MODE_MONO) { for (i = 1; i < 8; i++) if (point_table[i].p != point_table[i].points) @@ -775,234 +1855,354 @@ ft_render (MDrawWindow win, int x, int y, } } -static MPlist * -ft_list (MFrame *frame, MPlist *plist, MFont *font, MSymbol language) +static int +ft_list (MFrame *frame, MPlist *plist, MFont *font, int maxnum) { - MPlist *pl, *p; - MSymbol family = font ? FONT_PROPERTY (font, MFONT_FAMILY) : Mnil; - char *lang = language != Mnil ? MSYMBOL_NAME (language) : NULL; + MPlist *pl = NULL, *p; + int num = 0; + MPlist *file_list = NULL; + MPlist *family_list = NULL, *capability_list = NULL; + MSymbol registry = Mnil; - if (! all_fonts_scaned) - { -#ifdef HAVE_FONTCONFIG - fc_list (Mnil); -#else - ft_list_all (); -#endif - all_fonts_scaned = 1; - } + MDEBUG_DUMP (" [FONT-FT] listing ", "", mdebug_dump_font (font)); - if (family == Mnil) - pl = ft_font_list; - else + if (font) { - pl = mplist_find_by_key (ft_font_list, family); - if (! pl) - return NULL; - } + MSymbol family; - MPLIST_DO (pl, pl) - { - MPLIST_DO (p, MPLIST_VAL (pl)) + registry = FONT_PROPERTY (font, MFONT_REGISTRY); + if (registry != Mnil && registry != Miso8859_1) { - MFTInfo *ft_info = MPLIST_VAL (p); + char *reg = MSYMBOL_NAME (registry); - if (lang && ft_info->languages && strstr (ft_info->languages, lang)) - continue; - if (font && ! mfont__match_p (&ft_info->font, font, MFONT_REGISTRY)) - continue; - mplist_push (plist, FONT_PROPERTY (&ft_info->font, MFONT_FAMILY), - &ft_info->font); + if (strncmp (reg, "unicode-", 8) + && strncmp (reg, "apple-roman", 11) + && (reg[0] < '0' || reg[0] > '9' || reg[1] != '-')) + goto done; } - if (family != Mnil) - break; - } - return plist; -} - - -/* Internal API */ - -MFontDriver mfont__ft_driver = - { ft_select, ft_open, ft_find_metric, ft_encode_char, ft_render, ft_list }; - -int -mfont__ft_init () -{ - int i; - - if (FT_Init_FreeType (&ft_library) != 0) - MERROR (MERROR_FONT_FT, -1); - - for (i = 0; i < ft_to_prop_size; i++) - ft_to_prop[i].len = strlen (ft_to_prop[i].ft_style); - - Municode_bmp = msymbol ("unicode-bmp"); - Municode_full = msymbol ("unicode-full"); - Miso10646_1 = msymbol ("iso10646-1"); - Miso8859_1 = msymbol ("iso8859-1"); - -#ifdef HAVE_FONTCONFIG - fc_generic_family_list = mplist (); - mplist_push (fc_generic_family_list, msymbol ("serif"), Mt); - mplist_push (fc_generic_family_list, msymbol ("sans-serif"), Mt); - mplist_push (fc_generic_family_list, msymbol ("monospace"), Mt); - mplist_push (fc_generic_family_list, msymbol ("sans"), Mt); - mplist_push (fc_generic_family_list, msymbol ("sans serif"), Mt); - mplist_push (fc_generic_family_list, msymbol ("mono"), Mt); -#endif - - return 0; -} -void -mfont__ft_fini () -{ - MPlist *plist, *p; + if (font->file != Mnil + && ! (file_list = ft_list_file (font->file))) + goto done; + family = FONT_PROPERTY (font, MFONT_FAMILY); + if (family != Mnil + && (family_list = MPLIST_PLIST (ft_list_family (family, 1))) + && MPLIST_TAIL_P (family_list)) + goto done; + if (font->capability != Mnil) + { + capability_list = ft_list_capability (font->capability); + if (! capability_list || MPLIST_TAIL_P (capability_list)) + goto done; + } + } - if (ft_font_list) + if (! file_list && ! family_list && ! capability_list) { - MPLIST_DO (plist, ft_font_list) + /* No restriction. Get all fonts. */ + pl = mplist (); + MPLIST_DO (family_list, ft_list_family (Mnil, 0)) + { + MPLIST_DO (p, MPLIST_PLIST (family_list)) + mplist_push (pl, MPLIST_KEY (p), MPLIST_VAL (p)); + } + } + else + { + if (file_list) + { + pl = mplist (); + mplist_push (pl, MPLIST_KEY (file_list), MPLIST_VAL (file_list)); + } + if (family_list) { - MPLIST_DO (p, MPLIST_VAL (plist)) + if (pl) + for (p = pl; ! MPLIST_TAIL_P (p);) + { + if (mplist_find_by_value (family_list, MPLIST_VAL (p))) + p = MPLIST_NEXT (p); + else + mplist_pop (p); + } + else { - MFTInfo *ft_info = MPLIST_VAL (p); - - M17N_OBJECT_UNREF (ft_info); + pl = mplist (); + MPLIST_DO (p, family_list) + mplist_push (pl, MPLIST_KEY (p), MPLIST_VAL (p)); + } + } + if (capability_list) + { + if (pl) + for (p = pl; ! MPLIST_TAIL_P (p);) + { + if (mplist_find_by_value (capability_list, MPLIST_VAL (p))) + p = MPLIST_NEXT (p); + else + mplist_pop (p); + } + else + { + pl = mplist (); + MPLIST_DO (p, capability_list) + mplist_push (pl, MPLIST_KEY (p), MPLIST_VAL (p)); } - M17N_OBJECT_UNREF (MPLIST_VAL (plist)); } - M17N_OBJECT_UNREF (ft_font_list); - ft_font_list = NULL; } - FT_Done_FreeType (ft_library); - all_fonts_scaned = 0; + + if (font + && (font->property[MFONT_WEIGHT] + font->property[MFONT_STYLE] + + font->property[MFONT_STRETCH] + font->size) > 0) + { + MSymbol weight = FONT_PROPERTY (font, MFONT_WEIGHT); + MSymbol style = FONT_PROPERTY (font, MFONT_STYLE); + MSymbol stretch = FONT_PROPERTY (font, MFONT_STRETCH); + int size = font->size; -#ifdef HAVE_FONTCONFIG - m17n_object_unref (fc_generic_family_list); -#endif + for (p = pl; ! MPLIST_TAIL_P (p); ) + { + MFontFT *ft_info = MPLIST_VAL (p); + + if ((weight != Mnil + && weight != FONT_PROPERTY (&ft_info->font, MFONT_WEIGHT)) + || (style != Mnil + && style != FONT_PROPERTY (&ft_info->font, MFONT_STYLE)) + || (stretch != Mnil + && stretch != FONT_PROPERTY (&ft_info->font, + MFONT_STRETCH)) + || (size > 0 + && ft_info->font.size > 0 + && ft_info->font.size != size)) + mplist_pop (p); + else + p = MPLIST_NEXT (p); + } + } -} + MPLIST_DO (p, pl) + { + mplist_push (plist, MPLIST_KEY (p), MPLIST_VAL (p)); + num++; + if (maxnum && maxnum <= num) + break; + } + M17N_OBJECT_UNREF (pl); + done: + MDEBUG_PRINT1 (" %d found\n", num); + return num; +} -#ifdef HAVE_FONTCONFIG -typedef struct +static void +ft_list_family_names (MFrame *frame, MPlist *plist) { - int fc_value; - char *m17n_value; -} FC_vs_M17N_font_prop; + MPlist *pl; -static FC_vs_M17N_font_prop fc_weight_table[] = - { { FC_WEIGHT_ULTRALIGHT, "extralight" }, - { FC_WEIGHT_LIGHT, "light" }, - { FC_WEIGHT_NORMAL, "normal" }, - { FC_WEIGHT_MEDIUM, "medium" }, - { FC_WEIGHT_DEMIBOLD, "demibold" }, - { FC_WEIGHT_EXTRABOLD, "extrabold" }, - { FC_WEIGHT_BLACK, "black" }, - { FC_WEIGHT_MEDIUM, NULL } }; + if (! ft_font_list) + { +#ifdef HAVE_FONTCONFIG + fc_init_font_list (); +#else /* not HAVE_FONTCONFIG */ + ft_init_font_list (); +#endif /* not HAVE_FONTCONFIG */ + } -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 } }; + MPLIST_DO (pl, ft_font_list) + { + MSymbol family = MPLIST_KEY (pl); + MPlist *p; -static FC_vs_M17N_font_prop fc_width_table[] = - { { FC_WIDTH_CONDENSED, "condensed" }, - { FC_WIDTH_SEMIEXPANDED, "semicondensed" }, - { FC_WIDTH_NORMAL, "normal" }, - { FC_WIDTH_SEMIEXPANDED, "semiexpanded" }, - { FC_WIDTH_EXPANDED, "expanded" }, - { FC_WIDTH_NORMAL, NULL } }; +#ifdef HAVE_FONTCONFIG + if (msymbol_get (family, Mgeneric_family) != Mnil) + continue; +#endif /* HAVE_FONTCONFIG */ + MPLIST_DO (p, plist) + { + MSymbol sym = MPLIST_SYMBOL (p); + if (sym == family) + break; + if (strcmp (MSYMBOL_NAME (sym), MSYMBOL_NAME (family)) > 0) + { + mplist_push (p, Msymbol, family); + break; + } + } + if (MPLIST_TAIL_P (p)) + mplist_push (p, Msymbol, family); + } +} -static MSymbol -fc_decode_prop (int val, FC_vs_M17N_font_prop *table) +static int +ft_check_capability (MRealizedFont *rfont, MSymbol capability) { - int i; + MFontFT *ft_info = (MFontFT *) rfont->font; + MRealizedFontFT *ft_rfont = rfont->info; + MFontCapability *cap = mfont__get_capability (capability); - for (i = 0; table[i].m17n_value; i++) - if (val <= table[i].fc_value) - msymbol ("table[i].m17n_value"); - return msymbol ("table[i - 1].m17n_value"); + if (cap->script_tag) + { + if (ft_check_cap_otf (ft_info, cap, ft_rfont->ft_face) < 0) + return -1; + } + else if (cap->script != Mnil + && ft_check_script (ft_info, cap->script, ft_rfont->ft_face) < 0) + return -1; + if (cap->language != Mnil + && ft_check_language (ft_info, cap->language, ft_rfont->ft_face) < 0) + return -1; + return 0; } -static int -fc_encode_prop (char *name, FC_vs_M17N_font_prop *table) +static MRealizedFont * +ft_encapsulate (MFrame *frame, MSymbol data_type, void *data) { - int i; + MFontFT *ft_info; + MRealizedFont *rfont; + MRealizedFontFT *ft_rfont; + FT_Face ft_face; - for (i = 0; table[i].m17n_value && strcmp (name, table[i].m17n_value); i++); - return table[i].fc_value; + if (data_type == Mfontconfig) + { +#ifdef HAVE_FONTCONFIG + FcPattern *pattern = data; + + if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &ft_face) + != FcResultMatch) + return NULL; + ft_info = fc_gen_font (pattern, NULL); +#else /* not HAVE_FONTCONFIG */ + return NULL; +#endif /* not HAVE_FONTCONFIG */ + } + else if (data_type == Mfreetype) + { + ft_face = data; + ft_info = ft_gen_font (ft_face); + } + else + return NULL; + + M17N_OBJECT (ft_rfont, free_ft_rfont, MERROR_FONT_FT); + ft_rfont->ft_face = ft_face; + ft_rfont->face_encapsulated = 1; + + MDEBUG_DUMP (" [FONT-FT] encapsulating ", (char *) ft_face->family_name,); + + MSTRUCT_CALLOC (rfont, MERROR_FONT_FT); + rfont->font = (MFont *) ft_info; + rfont->info = ft_rfont; + rfont->fontp = ft_face; + rfont->driver = &mfont__ft_driver; + rfont->spec = ft_info->font; + rfont->spec.type = MFONT_TYPE_REALIZED; + rfont->frame = frame; + rfont->ascent = ft_face->size->metrics.ascender >> 6; + rfont->descent = - ft_face->size->metrics.descender >> 6; + rfont->max_advance = ft_face->size->metrics.max_advance >> 6; + rfont->baseline_offset = 0; + rfont->x_ppem = ft_face->size->metrics.x_ppem; + rfont->y_ppem = ft_face->size->metrics.y_ppem; +#ifdef HAVE_FTBDF_H + { + BDF_PropertyRec prop; + + if (! FT_IS_SCALABLE (ft_face) + && FT_Get_BDF_Property (ft_face, "_MULE_BASELINE_OFFSET", &prop) == 0) + { + rfont->baseline_offset = prop.u.integer << 6; + rfont->ascent += prop.u.integer << 6; + rfont->descent -= prop.u.integer << 6; + } + } +#endif /* HAVE_FTBDF_H */ + if (FT_IS_SCALABLE (ft_face)) + rfont->average_width = 0; + else + rfont->average_width = ft_face->available_sizes->width << 6; + rfont->next = MPLIST_VAL (frame->realized_font_list); + MPLIST_VAL (frame->realized_font_list) = rfont; + + return rfont; } -int -mfont__ft_parse_name (char *name, MFont *font) +static void +ft_close (MRealizedFont *rfont) { - FcPattern *pat = FcNameParse ((FcChar8 *) name); - FcChar8 *str; - int val; - double size; - - if (! pat) - return -1; - if (FcPatternGetString (pat, FC_FOUNDRY, 0, &str) == FcResultMatch) - mfont__set_property (font, MFONT_FOUNDRY, msymbol ((char *) str)); - if (FcPatternGetString (pat, FC_FAMILY, 0, &str) == FcResultMatch) - mfont__set_property (font, MFONT_FAMILY, msymbol ((char *) str)); - if (FcPatternGetInteger (pat, FC_WEIGHT, 0, &val) == FcResultMatch) - mfont__set_property (font, MFONT_WEIGHT, - fc_decode_prop (val, fc_weight_table)); - if (FcPatternGetInteger (pat, FC_SLANT, 0, &val) == FcResultMatch) - mfont__set_property (font, MFONT_STYLE, - fc_decode_prop (val, fc_slant_table)); - if (FcPatternGetInteger (pat, FC_WIDTH, 0, &val) == FcResultMatch) - mfont__set_property (font, MFONT_STRETCH, - fc_decode_prop (val, fc_width_table)); - if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size) == FcResultMatch) - font->property[MFONT_SIZE] = size * 10; - FcPatternDestroy (pat); - return 0; + if (! rfont->encapsulating) + return; + free (rfont->font); + M17N_OBJECT_UNREF (rfont->info); + free (rfont); } -char * -mfont__ft_unparse_name (MFont *font) +static int +ft_check_otf (MFLTFont *font, MFLTOtfSpec *spec) { - FcPattern *pat = FcPatternCreate (); - MSymbol sym, weight, style, stretch; - char *name; +#ifdef HAVE_OTF + MRealizedFont *rfont = ((MFLTFontForRealized *) font)->rfont; + MFontFT *ft_info = (MFontFT *) rfont->font; + OTF *otf; + OTF_Tag *tags; + int i, n, negative; - 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 (MSYMBOL_NAME (weight), - fc_weight_table)); - if ((style = (MSymbol) FONT_PROPERTY (font, MFONT_STYLE)) != Mnil) - FcPatternAddInteger (pat, FC_SLANT, fc_encode_prop (MSYMBOL_NAME (style), - fc_slant_table)); - if ((stretch = (MSymbol) FONT_PROPERTY (font, MFONT_STRETCH)) != Mnil) - FcPatternAddInteger (pat, FC_WIDTH, fc_encode_prop (MSYMBOL_NAME (stretch), - fc_width_table)); - name = (char *) FcNameUnparse (pat); - FcPatternDestroy (pat); - return name; + if (ft_info->otf == invalid_otf) + goto not_otf; + otf = ft_info->otf; + if (! otf) + { + MRealizedFontFT *ft_rfont = rfont->info; + +#if (LIBOTF_MAJOR_VERSION > 0 || LIBOTF_MINOR_VERSION > 9 || LIBOTF_RELEASE_NUMBER > 4) + otf = OTF_open_ft_face (ft_rfont->ft_face); +#else + otf = OTF_open (MSYMBOL_NAME (ft_info->font.file)); +#endif + if (! otf) + { + ft_info->otf = invalid_otf; + goto not_otf; + } + ft_info->otf = otf; + } + + for (i = 0; i < 2; i++) + { + if (! spec->features[i]) + continue; + for (n = 0; spec->features[i][n]; n++); + tags = alloca (sizeof (OTF_Tag) * n); + for (n = 0, negative = 0; spec->features[i][n]; n++) + { + if (spec->features[i][n] == 0xFFFFFFFF) + negative = 1; + else if (negative) + tags[n - 1] = spec->features[i][n] | 0x80000000; + else + tags[n] = spec->features[i][n]; + } + if (n - negative > 0 + && OTF_check_features (otf, i == 0, spec->script, spec->langsys, + tags, n - negative) != 1) + return 0; + } + return 1; +#endif /* HAVE_OTF */ + not_otf: + return ((! spec->features[0] || spec->features[0][0] == 0xFFFFFFFF) + && (! spec->features[1] || spec->features[1][0] == 0xFFFFFFFF)); } -#endif /* HAVE_FONTCONFIG */ - #ifdef HAVE_OTF #define DEVICE_DELTA(table, size) \ (((size) >= (table).StartSize && (size) <= (table).EndSize) \ - ? (table).DeltaValue[(size) >= (table).StartSize] \ + ? (table).DeltaValue[(size) - (table).StartSize] << 6 \ : 0) void adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face, - unsigned code, int size, int *x, int *y) + unsigned code, int x_ppem, int y_ppem, int *x, int *y) { if (anchor->AnchorFormat == 2) { @@ -1013,157 +2213,165 @@ adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face, outline = &ft_face->glyph->outline; if (ap < outline->n_points) { - *x = outline->points[ap].x; - *y = outline->points[ap].y; + *x = outline->points[ap].x << 6; + *y = outline->points[ap].y << 6; } } else if (anchor->AnchorFormat == 3) { - *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, size); - *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, size); + if (anchor->f.f2.XDeviceTable.offset) + *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, x_ppem); + if (anchor->f.f2.YDeviceTable.offset) + *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, y_ppem); } } +#endif /* HAVE_OTF */ -int -mfont__ft_drive_otf (MGlyphString *gstring, int from, int to, - MSymbol script, MSymbol langsys, - MSymbol gsub_features, MSymbol gpos_features) +static int +ft_drive_otf (MFLTFont *font, MFLTOtfSpec *spec, + MFLTGlyphString *in, int from, int to, + MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment) { int len = to - from; - MGlyph *g = MGLYPH (from); - int i, gidx; - MRealizedFont *rfont; - MFTInfo *ft_info; + int i, j, gidx; + MRealizedFont *rfont = ((MFLTFontForRealized *) font)->rfont; + MRealizedFontFT *ft_rfont = rfont->info; + MFontFT *ft_info = (MFontFT *) rfont->font; +#ifdef HAVE_OTF + MGlyph *in_glyphs = (MGlyph *) (in->glyphs); + MGlyph *out_glyphs = (MGlyph *) (out->glyphs); OTF *otf; OTF_GlyphString otf_gstring; OTF_Glyph *otfg; - char *script_name, *language_name; - char *gsub_feature_names, *gpos_feature_names; - int need_cmap; + char script[5], *langsys = NULL; + char *gsub_features = NULL, *gpos_features = NULL; if (len == 0) return from; - - rfont = g->rface->rfont; - ft_info = rfont->info; - if (ft_info->otf_flag < 0) + if (ft_info->otf == invalid_otf) goto simple_copy; otf = ft_info->otf; if (! otf) { - otf = OTF_open (ft_info->filename); - if (otf && OTF_get_table (otf, "head") < 0) - { - OTF_close (otf); - otf = NULL; - } + MRealizedFontFT *ft_rfont = rfont->info; + +#if (LIBOTF_MAJOR_VERSION > 0 || LIBOTF_MINOR_VERSION > 9 || LIBOTF_RELEASE_NUMBER > 4) + otf = OTF_open_ft_face (ft_rfont->ft_face); +#else + otf = OTF_open (MSYMBOL_NAME (ft_info->font.file)); +#endif if (! otf) { - ft_info->otf_flag = -1; + ft_info->otf = invalid_otf; goto simple_copy; } ft_info->otf = otf; } - if (script != Mnil) - script_name = msymbol_name (script); - else - script_name = NULL; - if (langsys != Mnil) - language_name = msymbol_name (langsys); - else - language_name = NULL; - gsub_feature_names - = (gsub_features == Mt ? "*" - : gsub_features == Mnil ? NULL - : msymbol_name (gsub_features)); - if (gsub_feature_names && OTF_check_table (otf, "GSUB") < 0) - gsub_feature_names = NULL; - gpos_feature_names - = (gpos_features == Mt ? "*" - : gpos_features == Mnil ? NULL - : msymbol_name (gpos_features)); - if (gpos_feature_names && OTF_check_table (otf, "GPOS") < 0) - gpos_feature_names = NULL; + if (OTF_get_table (otf, "head") < 0) + { + OTF_close (otf); + ft_info->otf = invalid_otf; + goto simple_copy; + } + + 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'; + } + } otf_gstring.size = otf_gstring.used = len; otf_gstring.glyphs = (OTF_Glyph *) malloc (sizeof (OTF_Glyph) * len); memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * len); - for (i = 0, need_cmap = 0; i < len; i++) + for (i = 0; i < len; i++) { - if (gstring->glyphs[from + i].otf_encoded) - { - otf_gstring.glyphs[i].c = gstring->glyphs[from + i].c; - otf_gstring.glyphs[i].glyph_id = gstring->glyphs[from + i].code; - } - else - { - otf_gstring.glyphs[i].c = gstring->glyphs[from + i].code; - need_cmap++; - } + otf_gstring.glyphs[i].c = ((MGlyph *)in->glyphs)[from + i].g.c; + otf_gstring.glyphs[i].glyph_id = ((MGlyph *)in->glyphs)[from + i].g.code; } - if (need_cmap - && OTF_drive_cmap (otf, &otf_gstring) < 0) - goto simple_copy; OTF_drive_gdef (otf, &otf_gstring); - gidx = gstring->used; + gidx = out->used; - if (gsub_feature_names) + if (gsub_features) { - if (OTF_drive_gsub (otf, &otf_gstring, script_name, language_name, - gsub_feature_names) < 0) + 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; i++, otfg++) { - MGlyph temp = *(MGLYPH (from + otfg->f.index.from)); + MGlyph *g = out_glyphs + out->used; + int j; - temp.c = otfg->c; - temp.combining_code = 0; - if (otfg->glyph_id) - { - temp.code = otfg->glyph_id; - temp.otf_encoded = 1; - } - else + *g = in_glyphs[from + otfg->f.index.from]; + g->g.c = 0; + for (j = from + otfg->f.index.from; j <= from + otfg->f.index.to; j++) + if (in_glyphs[j].g.code == otfg->glyph_id) + { + g->g.c = in_glyphs[j].g.c; + break; + } + if (g->g.code != otfg->glyph_id) { - temp.code = temp.c; - temp.otf_encoded = 0; + g->g.code = otfg->glyph_id; + g->g.measured = 0; } - temp.to = MGLYPH (from + otfg->f.index.to)->to; - MLIST_APPEND1 (gstring, glyphs, temp, MERROR_FONT_OTF); + out->used++; } } else - for (i = 0; i < len; i++) - { - MGlyph temp = gstring->glyphs[from + i]; - - if (otf_gstring.glyphs[i].glyph_id) - { - temp.code = otf_gstring.glyphs[i].glyph_id; - temp.otf_encoded = 1; - } - MLIST_APPEND1 (gstring, glyphs, temp, MERROR_FONT_OTF); - } - - ft_find_metric (rfont, gstring, gidx, gstring->used); + { + if (out->allocated < out->used + len) + return -2; + for (i = 0; i < len; i++) + out_glyphs[out->used++] = in_glyphs[from + i]; + } - if (gpos_feature_names) + if (gpos_features) { - int u; - int size10, size; - MGlyph *base = NULL, *mark = NULL; + FT_Face face; + MGlyph *base = NULL, *mark = NULL, *g; + int x_ppem, y_ppem, x_scale, y_scale; - if (OTF_drive_gpos (otf, &otf_gstring, script_name, language_name, - gpos_feature_names) < 0) + if (OTF_drive_gpos (otf, &otf_gstring, script, langsys, gpos_features) + < 0) return to; - u = otf->head->unitsPerEm; - size10 = rfont->font.property[MFONT_SIZE]; - size = size10 / 10; + face = ft_rfont->ft_face; + x_ppem = face->size->metrics.x_ppem; + y_ppem = face->size->metrics.y_ppem; + x_scale = face->size->metrics.x_scale; + y_scale = face->size->metrics.y_scale; - for (i = 0, otfg = otf_gstring.glyphs, g = MGLYPH (gidx); + for (i = 0, otfg = otf_gstring.glyphs, g = out_glyphs + gidx; i < otf_gstring.used; i++, otfg++, g++) { MGlyph *prev; @@ -1174,33 +2382,48 @@ mfont__ft_drive_otf (MGlyphString *gstring, int from, int to, { case 0: break; - case 1: case 2: + case 1: /* Single */ + case 2: /* Pair */ { int format = otfg->f.f1.format; if (format & OTF_XPlacement) - g->xoff = otfg->f.f1.value->XPlacement * size10 / u / 10; + adjustment[i].xoff + = otfg->f.f1.value->XPlacement * x_scale / 0x10000; if (format & OTF_XPlaDevice) - g->xoff += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, size); + adjustment[i].xoff + += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, x_ppem); if (format & OTF_YPlacement) - g->yoff = - (otfg->f.f1.value->YPlacement * size10 / u / 10); + adjustment[i].yoff + = - (otfg->f.f1.value->YPlacement * y_scale / 0x10000); if (format & OTF_YPlaDevice) - g->yoff -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, size); + adjustment[i].yoff + -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, y_ppem); if (format & OTF_XAdvance) - g->width += otfg->f.f1.value->XAdvance * size10 / u / 10; + adjustment[i].xadv + += otfg->f.f1.value->XAdvance * x_scale / 0x10000; if (format & OTF_XAdvDevice) - g->width += DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, size); + 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: + case 3: /* Cursive */ /* Not yet supported. */ break; - case 4: case 5: + case 4: /* Mark-to-Base */ + case 5: /* Mark-to-Ligature */ if (! base) break; prev = base; goto label_adjust_anchor; - default: /* i.e. case 6 */ + default: /* i.e. case 6 Mark-to-Mark */ if (! mark) break; prev = mark; @@ -1208,21 +2431,39 @@ mfont__ft_drive_otf (MGlyphString *gstring, int from, int to, 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 * size10 / u / 10; - base_y = otfg->f.f4.base_anchor->YCoordinate * size10 / u / 10; - mark_x = otfg->f.f4.mark_anchor->XCoordinate * size10 / u / 10; - mark_y = otfg->f.f4.mark_anchor->YCoordinate * size10 / u / 10; + 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 (otfg->f.f4.base_anchor, ft_info->ft_face, - prev->code, size, &base_x, &base_y); + adjust_anchor (otfg->f.f4.base_anchor, face, prev->g.code, + x_ppem, y_ppem, &base_x, &base_y); if (otfg->f.f4.mark_anchor->AnchorFormat != 1) - adjust_anchor (otfg->f.f4.mark_anchor, ft_info->ft_face, - g->code, size, &mark_x, &mark_y); - g->xoff = prev->xoff + (base_x - prev->width) - mark_x; - g->yoff = prev->yoff + mark_y - base_y; - g->combining_code = MAKE_PRECOMPUTED_COMBINDING_CODE (); + adjust_anchor (otfg->f.f4.mark_anchor, face, g->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->g.from; + this_to = g->g.to; + for (j = 0; prev + j < g; j++) + { + if (this_from > prev[j].g.from) + this_from = prev[j].g.from; + if (this_to < prev[j].g.to) + this_to = prev[j].g.to; + } + for (; prev <= g; prev++) + { + prev->g.from = this_from; + prev->g.to = this_to; + } } } if (otfg->GlyphClass == OTF_GlyphClass0) @@ -1237,26 +2478,226 @@ mfont__ft_drive_otf (MGlyphString *gstring, int from, int to, return to; simple_copy: - ft_find_metric (rfont, gstring, from, to); - for (i = 0; i < len; i++) - { - MGlyph temp = gstring->glyphs[from + i]; - MLIST_APPEND1 (gstring, glyphs, temp, MERROR_FONT_OTF); - } - free (otf_gstring.glyphs); +#endif /* HAVE_OTF */ + if (out->allocated < out->used + len) + return -2; + font->get_metrics (font, in, from, to); + memcpy ((MGlyph *)out->glyphs + out->used, (MGlyph *) in->glyphs + from, + sizeof (MGlyph) * len); + out->used += len; + if (otf_gstring.glyphs) + free (otf_gstring.glyphs); return to; } + +/* Internal API */ + +MFontDriver mfont__ft_driver = + { ft_select, ft_open, ft_find_metric, ft_has_char, ft_encode_char, + ft_render, ft_list, ft_list_family_names, ft_check_capability, + ft_encapsulate, ft_close, ft_check_otf, ft_drive_otf }; int -mfont__ft_decode_otf (MGlyph *g) +mfont__ft_init () { - MFTInfo *ft_info = (MFTInfo *) g->rface->rfont->info; - int c = OTF_get_unicode (ft_info->otf, (OTF_GlyphID) g->code); + int i; + + if (FT_Init_FreeType (&ft_library) != 0) + MERROR (MERROR_FONT_FT, -1); + + for (i = 0; i < ft_to_prop_size; i++) + ft_to_prop[i].len = strlen (ft_to_prop[i].ft_style); + + Mmedium = msymbol ("medium"); + Mr = msymbol ("r"); + Mnull = msymbol (""); + + M0[0] = msymbol ("0-0"); + M0[1] = msymbol ("0-1"); + M0[2] = msymbol ("0-2"); + M0[3] = msymbol ("0-3"); + M0[4] = msymbol ("0-4"); + M3_1 = msymbol ("3-1"); + M1_0 = msymbol ("1-0"); + +#ifdef HAVE_FONTCONFIG + for (i = 0; i < (sizeof (fc_all_table) / sizeof fc_all_table[0]); i++) + { + FC_vs_M17N_font_prop *table = fc_all_table[i]; + int j; + + for (j = 0; table[j].m17n_value; j++) + table[j].sym = msymbol (table[j].m17n_value); + table[j].sym = table[j - 1].sym; + } + + { + char *pathname; + struct stat buf; + MPlist *plist; + MSymbol serif, sans_serif, monospace; + + fc_config = FcInitLoadConfigAndFonts (); + if (mfont_freetype_path) + { + MPLIST_DO (plist, mfont_freetype_path) + if (MPLIST_STRING_P (plist) + && (pathname = MPLIST_STRING (plist)) + && stat (pathname, &buf) == 0) + { + FcStrList *strlist = FcConfigGetFontDirs (fc_config); + FcChar8 *dir; + + while ((dir = FcStrListNext (strlist))) + if (strcmp ((char *) dir, pathname) == 0) + break; + if (! dir) + FcConfigAppFontAddDir (fc_config, (FcChar8 *) pathname); + FcStrListDone (strlist); + } + } + Mgeneric_family = msymbol ("generic famly"); + serif = msymbol ("serif"); + msymbol_put (serif, Mgeneric_family, serif); + sans_serif = msymbol ("sans-serif"); + msymbol_put (sans_serif, Mgeneric_family, sans_serif); + msymbol_put (msymbol ("sans serif"), Mgeneric_family, sans_serif); + msymbol_put (msymbol ("sans"), Mgeneric_family, sans_serif); + monospace = msymbol ("monospace"); + msymbol_put (monospace, Mgeneric_family, monospace); + msymbol_put (msymbol ("mono"), Mgeneric_family, monospace); + } +#endif /* HAVE_FONTCONFIG */ - return (c ? c : -1); + return 0; } -#endif /* HAVE_OTF */ +void +mfont__ft_fini () +{ + MPlist *plist, *p; + + if (ft_default_list) + { + M17N_OBJECT_UNREF (ft_default_list); + ft_default_list = NULL; + } + + if (ft_font_list) + { + MPLIST_DO (plist, ft_font_list) + { + if (MPLIST_VAL (plist)) + MPLIST_DO (p, MPLIST_VAL (plist)) + { + if (MPLIST_KEY (p) != Mt) + free_ft_info (MPLIST_VAL (p)); + } + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + } + M17N_OBJECT_UNREF (ft_font_list); + ft_font_list = NULL; + + if (ft_language_list) + { + MPLIST_DO (plist, ft_language_list) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (ft_language_list); + ft_language_list = NULL; + } + + if (ft_script_list) + { + MPLIST_DO (plist, ft_script_list) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (ft_script_list); + ft_script_list = NULL; + } + + if (ft_capability_list) + { + MPLIST_DO (plist, ft_capability_list) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (ft_capability_list); + ft_capability_list = NULL; + } + + if (ft_file_list) + { + MPLIST_DO (plist, ft_file_list) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (ft_file_list); + ft_file_list = NULL; + } + } + FT_Done_FreeType (ft_library); +#ifdef HAVE_FONTCONFIG + FcConfigDestroy (fc_config); + fc_config = NULL; +#endif /* HAVE_FONTCONFIG */ + all_fonts_scaned = 0; +} + +#ifdef HAVE_FONTCONFIG + +int +mfont__ft_parse_name (const char *name, MFont *font) +{ + FcPattern *pat = FcNameParse ((FcChar8 *) name); + FcChar8 *str; + int val; + double size; + char *buf; + int bufsize = 0; + + if (! pat) + return -1; + if (FcPatternGetString (pat, FC_FOUNDRY, 0, &str) == FcResultMatch) + { + STRDUP_LOWER (buf, bufsize, (char *) str); + mfont__set_property (font, MFONT_FOUNDRY, msymbol (buf)); + } + 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) + mfont__set_property (font, MFONT_WEIGHT, + fc_decode_prop (val, fc_weight_table, + fc_weight_table_size)); + if (FcPatternGetInteger (pat, FC_SLANT, 0, &val) == FcResultMatch) + mfont__set_property (font, MFONT_STYLE, + fc_decode_prop (val, fc_slant_table, + fc_slant_table_size)); + if (FcPatternGetInteger (pat, FC_WIDTH, 0, &val) == FcResultMatch) + mfont__set_property (font, MFONT_STRETCH, + fc_decode_prop (val, fc_width_table, + fc_width_table_size)); + if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size) == FcResultMatch) + font->size = size * 10 + 0.5; + else if (FcPatternGetDouble (pat, FC_SIZE, 0, &size) == FcResultMatch) + font->size = - (size * 10 + 0.5); + if (FcPatternGetString (pat, FC_FILE, 0, &str) == FcResultMatch) + { + font->file = msymbol ((char *) str); + } + mfont__set_property (font, MFONT_REGISTRY, Municode_bmp); + font->type = MFONT_TYPE_SPEC; + FcPatternDestroy (pat); + return 0; +} + +char * +mfont__ft_unparse_name (MFont *font) +{ + FcPattern *pat = fc_get_pattern (font); + char *name = (char *) FcNameUnparse (pat); + + FcPatternDestroy (pat); + return name; +} +#endif /* HAVE_FONTCONFIG */ #endif /* HAVE_FREETYPE */