/* font-ft.c -- FreeType interface sub-module.
- Copyright (C) 2003, 2004
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
National Institute of Advanced Industrial Science and Technology (AIST)
Registration Number H15PRO112
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"
#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
#ifdef HAVE_FTBDF_H
-#include <freetype/ftbdf.h>
+#include FT_BDF_H
#endif
-static int mdebug_mask = MDEBUG_FONT;
+static int mdebug_flag = MDEBUG_FONT;
#ifdef HAVE_FONTCONFIG
+#include <fontconfig/fcfreetype.h>
+
static FcConfig *fc_config;
static MSymbol Mgeneric_family;
#endif /* HAVE_FONTCONFIG */
/* Font properties; Mnormal is already defined in face.c. */
static MSymbol Mmedium, Mr, Mnull;
-static MSymbol M0_3, M3_1, M1_0;
+static MSymbol M0[5], M3_1, M1_0;
static FT_Library ft_library;
#ifdef HAVE_OTF
static OTF *invalid_otf = (OTF *) "";
+static OTF *get_otf (MFLTFont *font, FT_Face *ft_face);
#endif /* HAVE_OTF */
typedef struct
{
MFont font;
- MPlist *lang;
#ifdef HAVE_OTF
/* NULL if not yet opened. invalid_otf if not OTF. */
OTF *otf;
typedef struct
{
M17NObject control;
- FT_Face ft_face;
+ FT_Face ft_face; /* This must be the 2nd member. */
MPlist *charmap_list;
+ int face_encapsulated;
} MRealizedFontFT;
typedef struct
(s1) = alloca (len), (size) = len; \
for (p1 = (s1), p2 = (s2); *p2; p1++, p2++) \
*p1 = (*p2 >= 'A' && *p2 <= 'Z' ? *p2 + 'a' - 'A' : *p2); \
- *p1 = *p2; \
+ *p1 = '\0'; \
} while (0)
+static MPlist *ft_list_family (MSymbol, int, int);
+
static void
free_ft_rfont (void *object)
{
MRealizedFontFT *ft_rfont = object;
- M17N_OBJECT_UNREF (ft_rfont->charmap_list);
- FT_Done_Face (ft_rfont->ft_face);
+ 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)
{
- M17N_OBJECT_UNREF (ft_info->lang);
#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);
}
if (ft_face->charmaps[i]->platform_id == 0)
{
- if (ft_face->charmaps[i]->encoding_id == 3)
- registry = M0_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)
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 (plist, registry, (void *) i);
}
if (unicode_full >= 0)
+ mplist_add (plist, Municode_full, (void *) unicode_full);
+ if (unicode_bmp >= 0)
{
- mplist_add (plist, Municode_full, (void *) unicode_full);
- mplist_add (plist, Municode_bmp, (void *) unicode_full);
- }
- else 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
#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" },
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, MFont *font)
+fc_parse_pattern (FcPattern *pat, char *family, MFontFT *ft_info)
{
FcChar8 *str;
int val;
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)
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;
MFontFT *ft_info;
MSTRUCT_CALLOC (ft_info, MERROR_FONT_FT);
- fc_parse_pattern (pat, family, &ft_info->font);
+ 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, 1));
+ 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 *
if (FT_New_Face (ft_library, filename, 0, &ft_face) != 0)
return NULL;
- if (! FT_IS_SCALABLE (ft_face))
- {
- int reject;
-#ifdef HAVE_FTBDF_H
- BDF_PropertyRec prop;
-
- reject = FT_Get_BDF_Property (ft_face, "PIXEL_SIZE", &prop) == 0;
- size = prop.u.integer * 10;
-#else /* not HAVE_FTBDF_H */
- reject = 1;
-#endif /* not HAVE_FTBDF_H */
- if (reject)
- {
- FT_Done_Face (ft_face);
- return NULL;
- }
- }
+ ft_info = ft_gen_font (ft_face);
+ FT_Done_Face (ft_face);
+ if (! ft_info)
+ return NULL;
- 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->type = MFONT_TYPE_OBJECT;
- font->source = MFONT_SOURCE_FT;
- font->size = size;
font->file = msymbol (filename);
- stylename = ft_face->style_name;
- while (*stylename)
- {
- 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++;
- }
-
- FT_Done_Face (ft_face);
-
plist = mplist_find_by_key (ft_font_list, family);
if (plist)
mplist_push (MPLIST_PLIST (plist), font->file, ft_info);
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, 1);
+
+ 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 */
scan all fonts and return ft_font_list. */
static MPlist *
-ft_list_family (MSymbol family, int check_generic)
+ft_list_family (MSymbol family, int check_generic, int check_alias)
{
MPlist *plist;
#ifdef HAVE_FONTCONFIG
MPLIST_DO (plist, ft_font_list)
{
if (! MPLIST_VAL (plist))
- ft_list_family (MPLIST_KEY (plist), 0);
+ ft_list_family (MPLIST_KEY (plist), 0, 1);
}
all_fonts_scaned = 1;
}
pattern = FcPatternCreate ();
FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) fam);
os = FcObjectSetBuild (FC_FOUNDRY, FC_WEIGHT, FC_SLANT, FC_WIDTH,
- FC_PIXEL_SIZE, FC_FILE, NULL);
+ 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++)
FcChar8 *fam8;
if (family != generic)
- plist = ft_list_family (generic, 1);
+ plist = ft_list_family (generic, 1, 1);
else
{
fam = MSYMBOL_NAME (family);
family = msymbol (buf);
if (msymbol_get (family, Mgeneric_family))
break;
- pl = ft_list_family (family, 0);
+ pl = ft_list_family (family, 0, 1);
if (! pl)
continue;
MPLIST_DO (pl, MPLIST_PLIST (pl))
plist = ft_font_list;
}
}
+ else if (check_alias)
+ {
+ /* 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, 0));
+ if (! MPLIST_TAIL_P (p))
+ MPLIST_DO (p, p)
+ mplist_push (pl, Mt, MPLIST_VAL (p));
+ }
+ }
+ }
else
- plist = mplist_add (ft_font_list, family, mplist ());
+ {
+ pl = mplist ();
+ plist = mplist_add (ft_font_list, family, pl);
+ }
#else /* not HAVE_FONTCONFIG */
if (! all_fonts_scaned)
{
- 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);
+ ft_init_font_list ();
all_fonts_scaned = 1;
}
if (family == Mnil)
{
MPlist *plist = NULL;
MText *mt;
- int step;
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);
-#ifdef HAVE_FONTCONFIG
- for (step = 0; step < 2; step++)
- {
- FcPattern *pattern = NULL;
- FcObjectSet *os = NULL;
- FcFontSet *fs = NULL;
- char *buf;
- int bufsize = 0;
- int i;
-
- if (! (pattern = FcPatternCreate ())
- || ! (os = FcObjectSetBuild (FC_FAMILY, FC_FILE, NULL)))
- goto err;
- if (step == 0)
- {
- FcLangSet *ls = FcLangSetCreate ();
-
- if (! ls)
- goto err;
- if (FcLangSetAdd (ls, (FcChar8 *) MSYMBOL_NAME (language))
- && FcPatternAddLangSet (pattern, FC_LANG, ls))
- fs = FcFontList (fc_config, pattern, os);
- FcLangSetDestroy (ls);
- if (! fs)
- goto err;
- }
- else
- {
- FcCharSet *cs;
-
- if (! (mt = msymbol_get (language, Mtext)))
- break;
- if (! (cs = FcCharSetCreate ()))
- goto err;
- for (i = mtext_nchars (mt) - 1; i >= 0; i--)
- if (! FcCharSetAddChar (cs, (FcChar32) mtext_ref_char (mt, i)))
- break;
- if (i < 0)
- {
- if (FcPatternAddCharSet (pattern, FC_CHARSET, cs))
- fs = FcFontList (fc_config, pattern, os);
- }
- FcCharSetDestroy (cs);
- if (! fs)
- goto err;
- }
-
- for (i = 0; i < fs->nfont; i++)
- {
- MSymbol family, file;
- char *fam, *filename;
- MPlist *pl;
- MFontFT *ft_info;
+ mt = mlanguage_text (language);
- 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);
- pl = MPLIST_PLIST (ft_list_family (family, 0));
- ft_info = mplist_get (pl, file);
- if (ft_info)
- {
- if (! plist)
- plist = mplist ();
- mplist_add (plist, family, ft_info);
- }
- }
- FcFontSetDestroy (fs);
- FcObjectSetDestroy (os);
- FcPatternDestroy (pattern);
- continue;
+#ifdef HAVE_FONTCONFIG
+ {
+ FcPattern *pattern = NULL;
+ FcCharSet *cs = NULL;
+ FcLangSet *ls = NULL;
- err:
- if (os)
- FcObjectSetDestroy (os);
- if (pattern)
- FcPatternDestroy (pattern);
- MEMORY_FULL (MERROR_FONT_FT);
- return NULL;
- }
+ if (! (pattern = FcPatternCreate ()))
+ goto err;
- mplist_push (ft_language_list, language, plist);
- return plist;
+ 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 = msymbol_get (language, Mtext)))
- {
- MPlist *pl, *p;
- int len = mtext_nchars (mt);
- int i;
-
- if (! ft_font_list)
- ft_list_family (Mnil, 0);
- MPLIST_DO (pl, ft_font_list)
- {
- MPLIST_DO (p, MPLIST_PLIST (pl))
- {
- MFontFT *ft_info = MPLIST_VAL (p);
- MSymbol family;
- FT_Face ft_face;
+ if (mt && mtext_nchars (mt) > 0)
+ plist = ft_list_char_list (NULL, mt);
+#endif /* not HAVE_FONTCONFIG */
- if (FT_New_Face (ft_library, MSYMBOL_NAME (ft_info->font.file),
- 0, &ft_face) != 0)
- continue;
- for (i = 0; i < len; i++)
- if (FT_Get_Char_Index (ft_face,
- (FT_ULong) mtext_ref_char (mt, i)) == 0)
- break;
- FT_Done_Face (ft_face);
- if (i < len)
- continue;
- if (! plist)
- plist = mplist ();
- family = mfont_get_prop (&ft_info->font, Mfamily);
- mplist_push (plist, family, ft_info);
- }
- }
- }
+ mplist_push (ft_language_list, language, plist);
return plist;
-#endif /* not HAVE_FONTCONFIG */
}
static MPlist *
ft_list_script (MSymbol script)
{
MPlist *plist = NULL;
- MPlist *language_list, *pl;
+ 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);
- language_list = mlanguage__list (script);
- MPLIST_DO (pl, language_list)
+ char_list = mscript__char_list (script);
+
+#ifdef HAVE_FONTCONFIG
+ if (char_list)
{
- MSymbol language = MPLIST_VAL (pl) ? MPLIST_SYMBOL (pl) : MPLIST_KEY (pl);
- MPlist *p = ft_list_language (language);
- MSymbol family;
+ FcPattern *pattern = NULL;
+ FcCharSet *cs;
- if (! p)
- continue;
- if (! plist)
- plist = mplist ();
- MPLIST_DO (p, p)
- {
- family = MPLIST_KEY (p);
- if (! mplist_find_by_value (plist, MPLIST_VAL (p)))
- mplist_add (plist, family, MPLIST_VAL (p));
- }
+ 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);
- M17N_OBJECT_UNREF (language_list);
return (plist);
}
static int
-ft_check_otf (MFontFT *ft_info, MFontCapability *cap)
+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)
{
- ft_info->otf = OTF_open (MSYMBOL_NAME (ft_info->font.file));
+#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;
}
}
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].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].nfeatures) != 1))
return -1;
return 0;
+#else /* not HAVE_OTF */
+ return -1;
+#endif /* not HAVE_OTF */
}
static int
-ft_check_lang (MFontFT *ft_info, MFontCapability *cap)
+ft_check_language (MFontFT *ft_info, MSymbol language, FT_Face ft_face)
{
-#ifdef HAVE_FONTCONFIG
- MPlist *plist;
MText *mt;
- int i, j;
+ 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;
- for (i = 0; cap->lang[i] != Mnil; i++)
+ if (! ft_face)
{
- if (ft_info->lang
- && (plist = mplist_find_by_key (ft_info->lang, cap->lang[i])))
- {
- if (MPLIST_VAL (plist))
- return 0;
- continue;
- }
+ char *filename = MSYMBOL_NAME (ft_info->font.file);
- if (! ft_info->langset)
- {
- FcPattern *pat = FcPatternBuild (NULL, FC_FILE, FcTypeString,
- MSYMBOL_NAME (ft_info->font.file),
- NULL);
- FcObjectSet *os = FcObjectSetBuild (FC_LANG, FC_CHARSET, NULL);
- FcFontSet *fs = FcFontList (fc_config, pat, os);
+ if (FT_New_Face (ft_library, filename, 0, &ft_face))
+ return -1;
+ ft_face_allocaed = 1;
+ }
- if (fs->nfont == 0)
- return -1;
- if (FcPatternGetLangSet (fs->fonts[0], FC_LANG, 0, &ft_info->langset)
- == FcResultMatch)
- ft_info->langset = FcLangSetCopy (ft_info->langset);
- else
- ft_info->langset = FcLangSetCreate ();
- FcPatternGetCharSet (fs->fonts[0], FC_CHARSET, 0, &ft_info->charset);
- FcFontSetDestroy (fs);
- FcObjectSetDestroy (os);
- FcPatternDestroy (pat);
- }
- if (! ft_info->lang)
- ft_info->lang = mplist ();
- if (FcLangSetHasLang (ft_info->langset,
- (FcChar8 *) MSYMBOL_NAME (cap->lang[i]))
- == FcLangEqual)
- {
- mplist_push (ft_info->lang, cap->lang[i], Mt);
- return 0;
- }
+ 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;
- mt = msymbol_get (cap->lang[i], Mtext);
- if (! mt)
+ if (! ft_face)
{
- mplist_push (ft_info->lang, cap->lang[i], Mnil);
- continue;
+ char *filename = MSYMBOL_NAME (ft_info->font.file);
+
+ if (FT_New_Face (ft_library, filename, 0, &ft_face))
+ return -1;
+ ft_face_allocaed = 1;
}
- for (j = mtext_nchars (mt) - 1; j >= 0; j--)
- if (! FcCharSetAddChar (ft_info->charset,
- (FcChar32) mtext_ref_char (mt, j)))
+ MPLIST_DO (char_list, char_list)
+ if (FT_Get_Char_Index (ft_face, (FT_ULong) MPLIST_INTEGER (char_list))
+ == 0)
break;
- mplist_push (ft_info->lang, cap->lang[i], (j < 0 ? Mt : Mnil));
- if (j < 0)
- return 0;
+ if (ft_face_allocaed)
+ FT_Done_Face (ft_face);
}
-#endif /* HAVE_FONTCONFIG */
- return -1;
+
+ 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, 1));
+ 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, 1))
+ {
+ 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 sym)
+ft_list_capability (MSymbol capability)
{
- MPlist *plist, *pl, *p;
- MFontCapability *cap = mfont__get_capability (sym);
+ MFontCapability *cap;
+ MPlist *plist = NULL, *pl;
- if (! cap)
- return NULL;
- if (ft_capability_list)
- {
- plist = mplist_find_by_key (ft_capability_list, sym);
- if (plist)
- return (MPLIST_VAL (plist) ? MPLIST_VAL (plist) : NULL);
- }
- else
- {
- plist = NULL;
- ft_capability_list = mplist ();
- }
+ if (! 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->script != Mnil)
+ if (cap && cap->language != Mnil)
{
- pl = ft_list_script (cap->script);
- if (pl)
- MPLIST_DO (pl, pl)
- {
- if (cap->script_tag && ft_check_otf (MPLIST_VAL (pl), cap) < 0)
- continue;
- if (cap->lang && ft_check_lang (MPLIST_VAL (pl), cap) < 0)
- continue;
- if (! plist)
- plist = mplist ();
- mplist_add (plist, MPLIST_KEY (pl), MPLIST_VAL (pl));
- }
- mplist_push (ft_capability_list, sym, plist);
- return plist;
+ plist = ft_list_language (cap->language);
+ if (! plist)
+ return NULL;
+ plist = mplist_copy (plist);
}
- if (cap->lang)
+ if (cap && cap->script != Mnil)
{
- int i;
+ 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);
+ }
+ }
- for (i = 0; cap->lang[i] != Mnil; i++)
+ if (cap->script_tag)
{
- p = ft_list_language (cap->lang[i]);
- if (p)
+ for (pl = plist; ! MPLIST_TAIL_P (pl);)
{
- if (! plist)
- plist = mplist ();
- MPLIST_DO (p, p)
- mplist_add (plist, MPLIST_KEY (p), MPLIST_VAL (p));
+ 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, sym, plist);
+
+ mplist_push (ft_capability_list, capability, plist);
return plist;
}
STRDUP_LOWER (buf, bufsize, fam);
family = msymbol (buf);
- pl = ft_list_family (family, 0);
+ pl = ft_list_family (family, 0, 1);
MPLIST_DO (pl, MPLIST_PLIST (pl))
{
MFontFT *ft_info = MPLIST_VAL (pl);
{
MPlist *pl, *p;
- MPLIST_DO (pl, ft_list_family (Mnil, 0))
+ MPLIST_DO (pl, ft_list_family (Mnil, 0, 1))
{
MPLIST_DO (p, MPLIST_PLIST (pl))
{
{
MSymbol family = FONT_PROPERTY (font, MFONT_FAMILY);
- if (! family)
- return NULL;
- plist = MPLIST_PLIST (ft_list_family (family, 1));
+ if (family)
+ plist = MPLIST_PLIST (ft_list_family (family, 1, 1));
+ else
+ plist = ft_list_default ();
if (MPLIST_TAIL_P (plist))
return NULL;
}
for (pl = plist; ! MPLIST_TAIL_P (pl);)
{
- if ((cap->script_tag && ft_check_otf (MPLIST_VAL (pl), cap) < 0)
- || (cap->lang && ft_check_lang (MPLIST_VAL (pl), cap) < 0))
+ 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);
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))
+ && (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
}
}
M17N_OBJECT_UNREF (plist);
-#endif
+#endif /* HAVE_FONTCONFIG */
return found;
}
FT_Face ft_face;
MPlist *plist, *charmap_list = NULL;
int charmap_index;
- int size = font->size ? font->size : spec->size;
+ int size;
+
+ if (font->size)
+ /* non-scalable font */
+ size = font->size;
+ else if (spec->size)
+ {
+ int ratio = mfont_resize_ratio (font);
+
+ size = ratio == 100 ? spec->size : spec->size * ratio / 100;
+ }
+ else
+ size = 120;
if (rfont)
{
return rfont;
}
- MDEBUG_PRINT3 (" [FONT-FT] opening %s-%d:file=%s ...",
- MSYMBOL_NAME ((MSymbol) mfont_get_prop (font, Mfamily)),
- size / 10, MSYMBOL_NAME (font->file));
+ 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 ("failed\n");
+ MDEBUG_PRINT (" no (FT_New_Face)\n");
return NULL;
}
if (charmap_list)
else
charmap_list = ft_get_charmaps (ft_face);
if (registry == Mnil)
- registry = Mt;
+ registry = Municode_bmp;
plist = mplist_find_by_key (charmap_list, registry);
if (! plist)
{
FT_Done_Face (ft_face);
M17N_OBJECT_UNREF (charmap_list);
- MDEBUG_PRINT ("failed\n");
+ MDEBUG_PRINT1 (" no (%s)\n", MSYMBOL_NAME (registry));
return NULL;
}
charmap_index = (int) MPLIST_VAL (plist);
- if (FT_Set_Charmap (ft_face, ft_face->charmaps[charmap_index])
+ 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_PRINT ("failed\n");
+ MDEBUG_PRINT1 (" no (size %d)\n", size);
return NULL;
}
ft_rfont->ft_face = ft_face;
ft_rfont->charmap_list = charmap_list;
MSTRUCT_CALLOC (rfont, MERROR_FONT_FT);
+ rfont->id = ft_info->font.file;
rfont->spec = *font;
rfont->spec.type = MFONT_TYPE_REALIZED;
rfont->spec.property[MFONT_REGISTRY] = reg;
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");
+ MDEBUG_PRINT (" ok\n");
return rfont;
}
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->spec.size / 10;
-
- g->lbearing = 0;
- g->rbearing = ft_face->max_advance_width * size / unitsPerEm;
- g->width = g->rbearing;
- 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
+#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
+#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;
}
}
}
{
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;
}
}
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_face = rface->rfont->fontp;
+ baseline_offset = rface->rfont->baseline_offset >> 6;
if (! gstring->anti_alias)
{
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;
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;
{
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)
{
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)));
{
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)
{
}
}
- 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)
MPlist *family_list = NULL, *capability_list = NULL;
MSymbol registry = Mnil;
- MDEBUG_PRINT2 (" [FONT-FT] listing %s%s...",
- FONT_PROPERTY (font, MFONT_FAMILY)
- ? msymbol_name (FONT_PROPERTY (font, MFONT_FAMILY)) : "*",
- font->capability ? msymbol_name (font->capability) : "");
+ MDEBUG_DUMP (" [FONT-FT] listing ", "", mdebug_dump_font (font));
if (font)
{
MSymbol family;
registry = FONT_PROPERTY (font, MFONT_REGISTRY);
- if (registry != Mnil)
+ if (registry != Mnil && registry != Miso8859_1)
{
char *reg = MSYMBOL_NAME (registry);
goto done;
family = FONT_PROPERTY (font, MFONT_FAMILY);
if (family != Mnil
- && (family_list = MPLIST_PLIST (ft_list_family (family, 1)))
+ && (family_list = MPLIST_PLIST (ft_list_family (family, 1, 1)))
&& MPLIST_TAIL_P (family_list))
goto done;
- if (font->capability != Mnil
- && (capability_list = ft_list_capability (font->capability))
- && MPLIST_TAIL_P (capability_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 (! file_list && ! family_list && ! capability_list)
{
/* No restriction. Get all fonts. */
pl = mplist ();
- MPLIST_DO (family_list, ft_list_family (Mnil, 0))
+ MPLIST_DO (family_list, ft_list_family (Mnil, 0, 1))
{
MPLIST_DO (p, MPLIST_PLIST (family_list))
mplist_push (pl, MPLIST_KEY (p), MPLIST_VAL (p));
if (pl)
for (p = pl; ! MPLIST_TAIL_P (p);)
{
- MFontFT *ft_info = MPLIST_VAL (p);
-
- if (mplist_find_by_value (capability_list, ft_info))
+ if (mplist_find_by_value (capability_list, MPLIST_VAL (p)))
p = MPLIST_NEXT (p);
else
mplist_pop (p);
}
}
- if (font)
+ 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;
- if (weight != Mnil || style != Mnil || stretch != Mnil || size > 0)
- 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);
- }
+ 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)
{
- plist = mplist_add (plist, MPLIST_KEY (p), MPLIST_VAL (p));
+ 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);
+ MDEBUG_PRINT1 (" %d found\n", num);
return num;
}
-\f
-/* Internal API */
-
-MFontDriver mfont__ft_driver =
- { ft_select, ft_open, ft_find_metric, ft_has_char, ft_encode_char,
- ft_render, ft_list };
-
-int
-mfont__ft_init ()
+static void
+ft_list_family_names (MFrame *frame, MPlist *plist)
{
- int i;
+ MPlist *pl;
- if (FT_Init_FreeType (&ft_library) != 0)
- MERROR (MERROR_FONT_FT, -1);
+ if (! ft_font_list)
+ {
+#ifdef HAVE_FONTCONFIG
+ fc_init_font_list ();
+#else /* not HAVE_FONTCONFIG */
+ ft_init_font_list ();
+#endif /* not HAVE_FONTCONFIG */
+ }
- for (i = 0; i < ft_to_prop_size; i++)
- ft_to_prop[i].len = strlen (ft_to_prop[i].ft_style);
+ MPLIST_DO (pl, ft_font_list)
+ {
+ MSymbol family = MPLIST_KEY (pl);
+ MPlist *p;
- Mmedium = msymbol ("medium");
- Mr = msymbol ("r");
- Mnull = msymbol ("");
+#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 int
+ft_check_capability (MRealizedFont *rfont, MSymbol capability)
+{
+ MFontFT *ft_info = (MFontFT *) rfont->font;
+ MRealizedFontFT *ft_rfont = rfont->info;
+ MFontCapability *cap = mfont__get_capability (capability);
+
+ 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 MRealizedFont *
+ft_encapsulate (MFrame *frame, MSymbol data_type, void *data)
+{
+ MFontFT *ft_info;
+ MRealizedFont *rfont;
+ MRealizedFontFT *ft_rfont;
+ FT_Face ft_face;
- M0_3 = msymbol ("0-3");
+ 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_PRINT1 (" [FONT-FT] encapsulating %s", (char *) ft_face->family_name);
+
+ MSTRUCT_CALLOC (rfont, MERROR_FONT_FT);
+ rfont->id = ft_info->font.file;
+ 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;
+}
+
+static void
+ft_close (MRealizedFont *rfont)
+{
+ if (! rfont->encapsulating)
+ return;
+ free (rfont->font);
+ M17N_OBJECT_UNREF (rfont->info);
+ free (rfont);
+}
+
+static int
+ft_check_otf (MFLTFont *font, MFLTOtfSpec *spec)
+{
+#ifdef HAVE_OTF
+ OTF_Tag *tags;
+ int i, n, negative;
+ OTF *otf = get_otf (font, NULL);
+
+ if (! otf)
+ goto not_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;
+ not_otf:
+#endif /* HAVE_OTF */
+ return ((! spec->features[0] || spec->features[0][0] == 0xFFFFFFFF)
+ && (! spec->features[1] || spec->features[1][0] == 0xFFFFFFFF));
+}
+
+#ifdef HAVE_OTF
+
+static OTF *
+get_otf (MFLTFont *font, FT_Face *ft_face)
+{
+ MRealizedFont *rfont = ((MFLTFontForRealized *) font)->rfont;
+ MFontFT *ft_info = (MFontFT *) rfont->font;
+ MRealizedFontFT *ft_rfont = rfont->info;
+ OTF *otf = ft_info->otf;
+
+ if (! otf)
+ {
+#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 || OTF_get_table (otf, "head") < 0)
+ otf = invalid_otf;
+ ft_info->otf = otf;
+ }
+ if (ft_face)
+ *ft_face = ft_rfont->ft_face;
+ return (otf == invalid_otf ? NULL : otf);
+}
+
+#define DEVICE_DELTA(table, size) \
+ (((size) >= (table).StartSize && (size) <= (table).EndSize) \
+ ? (table).DeltaValue[(size) - (table).StartSize] << 6 \
+ : 0)
+
+void
+adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face,
+ unsigned code, int x_ppem, int y_ppem, int *x, int *y)
+{
+ if (anchor->AnchorFormat == 2)
+ {
+ FT_Outline *outline;
+ int ap = anchor->f.f1.AnchorPoint;
+
+ FT_Load_Glyph (ft_face, (FT_UInt) code, FT_LOAD_MONOCHROME);
+ outline = &ft_face->glyph->outline;
+ if (ap < outline->n_points)
+ {
+ *x = outline->points[ap].x << 6;
+ *y = outline->points[ap].y << 6;
+ }
+ }
+ else if (anchor->AnchorFormat == 3)
+ {
+ 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 */
+
+static int
+ft_drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
+ MFLTGlyphString *in, int from, int to,
+ MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
+{
+ int len = to - from;
+#ifdef HAVE_OTF
+ int i, j, gidx;
+ MGlyph *in_glyphs = (MGlyph *) (in->glyphs);
+ MGlyph *out_glyphs = out ? (MGlyph *) (out->glyphs) : NULL;
+ OTF *otf;
+ FT_Face face;
+ OTF_GlyphString otf_gstring;
+ OTF_Glyph *otfg;
+ char script[5], *langsys = NULL;
+ char *gsub_features = NULL, *gpos_features = NULL;
+ unsigned int tag;
+
+ if (len == 0)
+ return from;
+ otf = get_otf (font, &face);
+ if (! 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])
+ {
+ 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; i < len; i++)
+ {
+ otf_gstring.glyphs[i].c = ((MGlyph *)in->glyphs)[from + i].g.c & 0x11FFFF;
+ otf_gstring.glyphs[i].glyph_id = ((MGlyph *)in->glyphs)[from + i].g.code;
+ }
+
+ OTF_drive_gdef (otf, &otf_gstring);
+ gidx = out ? out->used : from;
+
+ if (gsub_features)
+ {
+ OTF_Feature *features;
+ MGlyph *g;
+
+ if (OTF_drive_gsub_with_log (otf, &otf_gstring, script, langsys,
+ gsub_features) < 0)
+ goto simple_copy;
+ features = otf->gsub->FeatureList.Feature;
+ if (out)
+ {
+ if (out->allocated < gidx + otf_gstring.used)
+ return -2;
+ for (i = 0, otfg = otf_gstring.glyphs, g = out_glyphs + gidx;
+ i < otf_gstring.used; i++, otfg++, g++, out->used++)
+ {
+ int feature_idx = otfg->positioning_type >> 4;
+ int j;
+ int min_from, max_to;
+
+ *g = in_glyphs[from + otfg->f.index.from];
+ min_from = g->g.from, max_to = g->g.to;
+ g->g.c = 0;
+ for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
+ if (in_glyphs[from + j].g.code == otfg->glyph_id)
+ {
+ g->g.c = in_glyphs[from + j].g.c;
+ break;
+ }
+ if (feature_idx)
+ {
+ tag = features[feature_idx - 1].FeatureTag;
+ tag = PACK_OTF_TAG (tag);
+ g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
+ }
+ for (j = otfg->f.index.from + 1; j <= otfg->f.index.to; j++)
+ {
+ if (min_from > in_glyphs[from + j].g.from)
+ min_from = in_glyphs[from + j].g.from;
+ if (max_to < in_glyphs[from + j].g.to)
+ max_to = in_glyphs[from + j].g.to;
+ }
+ if (g->g.code != otfg->glyph_id)
+ {
+ g->g.code = otfg->glyph_id;
+ g->g.measured = 0;
+ }
+ g->g.from = min_from, g->g.to = max_to;
+ }
+ }
+ else
+ {
+ for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used;
+ i++, otfg++)
+ {
+ int feature_idx = otfg->positioning_type >> 4;
+
+ if (feature_idx)
+ {
+ tag = features[feature_idx - 1].FeatureTag;
+ tag = PACK_OTF_TAG (tag);
+ for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
+ {
+ g = in_glyphs + (from + j);
+ g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
+ }
+ }
+ }
+ }
+ }
+ else if (out)
+ {
+ if (out->allocated < gidx + len)
+ return -2;
+ for (i = 0; i < len; i++)
+ out_glyphs[out->used++] = in_glyphs[from + i];
+ }
+
+ if (gpos_features)
+ {
+ OTF_Feature *features;
+ MGlyph *g;
+
+ if (OTF_drive_gpos_with_log (otf, &otf_gstring, script, langsys,
+ gpos_features) < 0)
+ return to;
+ features = otf->gpos->FeatureList.Feature;
+ if (out)
+ {
+ MGlyph *base = NULL, *mark = NULL;
+ int x_ppem = face->size->metrics.x_ppem;
+ int y_ppem = face->size->metrics.y_ppem;
+ int x_scale = face->size->metrics.x_scale;
+ int y_scale = face->size->metrics.y_scale;
+
+ for (i = j = 0, otfg = otf_gstring.glyphs, g = out_glyphs + gidx;
+ i < otf_gstring.used; i++, otfg++)
+ {
+ MGlyph *prev;
+ int adjust_idx = otfg->glyph_id ? j : j - 1;
+ int feature_idx = otfg->positioning_type >> 4;
+
+ if (feature_idx)
+ {
+ tag = features[feature_idx - 1].FeatureTag;
+ tag = PACK_OTF_TAG (tag);
+ g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
+ }
+ switch (otfg->positioning_type & 0xF)
+ {
+ case 0:
+ break;
+ case 1: /* Single */
+ case 2: /* Pair */
+ {
+ int format = otfg->f.f1.format;
+
+ if (format & OTF_XPlacement)
+ adjustment[adjust_idx].xoff
+ += otfg->f.f1.value->XPlacement * x_scale / 0x10000;
+ if (format & OTF_XPlaDevice)
+ adjustment[adjust_idx].xoff
+ += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, x_ppem);
+ if (format & OTF_YPlacement)
+ adjustment[adjust_idx].yoff
+ -= otfg->f.f1.value->YPlacement * y_scale / 0x10000;
+ if (format & OTF_YPlaDevice)
+ adjustment[adjust_idx].yoff
+ -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, y_ppem);
+ if (format & OTF_XAdvance)
+ adjustment[adjust_idx].xadv
+ += otfg->f.f1.value->XAdvance * x_scale / 0x10000;
+ if (format & OTF_XAdvDevice)
+ adjustment[adjust_idx].xadv
+ += DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, x_ppem);
+ if (format & OTF_YAdvance)
+ adjustment[adjust_idx].yadv
+ += otfg->f.f1.value->YAdvance * y_scale / 0x10000;
+ if (format & OTF_YAdvDevice)
+ adjustment[adjust_idx].yadv
+ += DEVICE_DELTA (otfg->f.f1.value->YAdvDevice, y_ppem);
+ adjustment[adjust_idx].set = 1;
+ }
+ break;
+ case 3: /* Cursive */
+ /* Not yet supported. */
+ break;
+ case 4: /* Mark-to-Base */
+ case 5: /* Mark-to-Ligature */
+ if (! base)
+ break;
+ prev = base;
+ goto label_adjust_anchor;
+ default: /* i.e. case 6 Mark-to-Mark */
+ if (! mark)
+ break;
+ prev = mark;
+
+ label_adjust_anchor:
+ {
+ int base_x, base_y, mark_x, mark_y;
+ int this_from, this_to;
+ int k;
+
+ 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, 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, face, g->g.code,
+ x_ppem, y_ppem, &mark_x, &mark_y);
+ adjustment[adjust_idx].xoff = base_x - mark_x;
+ adjustment[adjust_idx].yoff = - (base_y - mark_y);
+ adjustment[adjust_idx].back = (g - prev);
+ adjustment[adjust_idx].xadv = 0;
+ adjustment[adjust_idx].advance_is_absolute = 1;
+ adjustment[adjust_idx].set = 1;
+ this_from = g->g.from;
+ this_to = g->g.to;
+ for (k = 0; prev + k < g; k++)
+ {
+ if (this_from > prev[k].g.from)
+ this_from = prev[k].g.from;
+ if (this_to < prev[k].g.to)
+ this_to = prev[k].g.to;
+ }
+ for (; prev <= g; prev++)
+ {
+ prev->g.from = this_from;
+ prev->g.to = this_to;
+ }
+ }
+ }
+ if (otfg->glyph_id)
+ {
+ if (otfg->GlyphClass == OTF_GlyphClass0)
+ base = mark = g;
+ else if (otfg->GlyphClass == OTF_GlyphClassMark)
+ mark = g;
+ else
+ base = g;
+ j++, g++;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used;
+ i++, otfg++)
+ if (otfg->positioning_type & 0xF)
+ {
+ int feature_idx = otfg->positioning_type >> 4;
+
+ if (feature_idx)
+ {
+ tag = features[feature_idx - 1].FeatureTag;
+ tag = PACK_OTF_TAG (tag);
+ for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
+ {
+ g = in_glyphs + (from + j);
+ g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
+ }
+ }
+ }
+ }
+ }
+
+ free (otf_gstring.glyphs);
+ return to;
+
+ simple_copy:
+ if (otf_gstring.glyphs)
+ free (otf_gstring.glyphs);
+#endif /* HAVE_OTF */
+ if (out)
+ {
+ 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;
+ }
+ return to;
+}
+
+static int
+ft_try_otf (MFLTFont *font, MFLTOtfSpec *spec,
+ MFLTGlyphString *in, int from, int to)
+{
+ return ft_drive_otf (font, spec, in, from, to, NULL, NULL);
+}
+
+
+#ifdef HAVE_OTF
+static unsigned char *iterate_bitmap;
+
+static int
+iterate_callback (OTF *otf, const char *feature, unsigned glyph_id)
+{
+ if (glyph_id <= otf->cmap->max_glyph_id)
+ iterate_bitmap[glyph_id / 8] |= 1 << (glyph_id % 8);
+ return 0;
+}
+
+static int
+ft_iterate_otf_feature (MFLTFont *font, MFLTOtfSpec *spec,
+ int from, int to, unsigned char *table)
+{
+ OTF *otf = get_otf (font, NULL);
+ char id[13];
+ int bmp_size;
+ unsigned char *bitmap = NULL;
+ int i, j;
+ char script[5], *langsys = NULL;
+
+ if (! otf)
+ return -1;
+ if (OTF_get_table (otf, "cmap") < 0)
+ return -1;
+ if (! spec->features[0])
+ return -1;
+ strcpy (id, "feature-");
+ id[12] = '\0';
+ OTF_tag_name (spec->script, script);
+ if (spec->langsys)
+ {
+ langsys = alloca (5);
+ OTF_tag_name (spec->langsys, langsys);
+ }
+ bmp_size = (otf->cmap->max_glyph_id / 8) + 1;
+ for (i = 0; spec->features[0][i]; i++)
+ {
+ unsigned char *bmp;
+
+ OTF_tag_name (spec->features[0][i], id + 8);
+ bmp = OTF_get_data (otf, id);
+ if (! bmp)
+ {
+ iterate_bitmap = bmp = calloc (bmp_size, 1);
+ OTF_iterate_gsub_feature (otf, iterate_callback,
+ script, langsys, id + 8);
+ OTF_put_data (otf, id, bmp, free);
+ }
+ if (i == 0 && ! spec->features[0][1])
+ /* Single feature */
+ bitmap = bmp;
+ else
+ {
+ if (! bitmap)
+ {
+ bitmap = alloca (bmp_size);
+ memcpy (bitmap, bmp, bmp_size);
+ }
+ else
+ {
+ int j;
+
+ for (j = 0; j < bmp_size; j++)
+ bitmap[j] &= bmp[j];
+ }
+ }
+ }
+ for (i = 0; i < bmp_size; i++)
+ if (bitmap[i])
+ {
+ for (j = 0; j < 8; j++)
+ if (bitmap[i] & (1 << j))
+ {
+ int c = OTF_get_unicode (otf, (i * 8) + j);
+
+ if (c >= from && c <= to)
+ table[c - from] = 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+
+\f
+/* 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, ft_try_otf,
+#ifdef HAVE_OTF
+ ft_iterate_otf_feature
+#endif /* HAVE_OTF */
+ };
+
+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);
+
+ 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");
msymbol_put (monospace, Mgeneric_family, monospace);
msymbol_put (msymbol ("mono"), Mgeneric_family, monospace);
}
-#endif
+#endif /* HAVE_FONTCONFIG */
return 0;
}
{
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)
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
#ifdef HAVE_FONTCONFIG
int
-mfont__ft_parse_name (char *name, MFont *font)
+mfont__ft_parse_name (const char *name, MFont *font)
{
FcPattern *pat = FcNameParse ((FcChar8 *) name);
FcChar8 *str;
fc_decode_prop (val, fc_width_table,
fc_width_table_size));
if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
- font->size = size * 10;
+ 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);
}
#endif /* HAVE_FONTCONFIG */
-\f
-#ifdef HAVE_OTF
-
-#define DEVICE_DELTA(table, size) \
- (((size) >= (table).StartSize && (size) <= (table).EndSize) \
- ? (table).DeltaValue[(size) >= (table).StartSize] \
- : 0)
-
-void
-adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face,
- unsigned code, int size, int *x, int *y)
-{
- if (anchor->AnchorFormat == 2)
- {
- FT_Outline *outline;
- int ap = anchor->f.f1.AnchorPoint;
-
- FT_Load_Glyph (ft_face, (FT_UInt) code, FT_LOAD_MONOCHROME);
- outline = &ft_face->glyph->outline;
- if (ap < outline->n_points)
- {
- *x = outline->points[ap].x;
- *y = outline->points[ap].y;
- }
- }
- else if (anchor->AnchorFormat == 3)
- {
- if (anchor->f.f2.XDeviceTable.offset)
- *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, size);
- if (anchor->f.f2.YDeviceTable.offset)
- *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, size);
- }
-}
-
-int
-mfont__ft_drive_otf (MGlyphString *gstring, int from, int to,
- MFontCapability *cap)
-{
- int len = to - from;
- MGlyph *g = MGLYPH (from);
- int i, gidx;
- MRealizedFont *rfont;
- MFontFT *ft_info;
- OTF *otf;
- OTF_GlyphString otf_gstring;
- OTF_Glyph *otfg;
- char *script, *langsys;
- char *gsub_features, *gpos_features;
- int need_cmap;
-
- if (len == 0)
- return from;
-
- otf_gstring.glyphs = NULL;
- rfont = g->rface->rfont;
- ft_info = (MFontFT *) rfont->font;
- if (ft_info->otf == invalid_otf)
- goto simple_copy;
- otf = ft_info->otf;
- if (! otf)
- {
- otf = OTF_open (MSYMBOL_NAME (ft_info->font.file));
- if (! otf)
- {
- ft_info->otf = invalid_otf;
- goto simple_copy;
- }
- ft_info->otf = otf;
- }
- if (OTF_get_table (otf, "head") < 0)
- {
- OTF_close (otf);
- ft_info->otf = invalid_otf;
- goto simple_copy;
- }
-
- if (cap->script_tag)
- {
- script = alloca (5);
- OTF_tag_name (cap->script_tag, script);
- }
- else
- script = NULL;
- if (cap->langsys_tag)
- {
- langsys = alloca (5);
- OTF_tag_name (cap->langsys_tag, langsys);
- }
- else
- langsys = NULL;
- gsub_features = cap->features[MFONT_OTT_GSUB].str;
- if (gsub_features && OTF_check_table (otf, "GSUB") < 0)
- gsub_features = NULL;
- gpos_features = cap->features[MFONT_OTT_GPOS].str;
- if (gpos_features && OTF_check_table (otf, "GPOS") < 0)
- gpos_features = NULL;
-
- 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++)
- {
- 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++;
- }
- }
- if (need_cmap
- && OTF_drive_cmap (otf, &otf_gstring) < 0)
- goto simple_copy;
-
- OTF_drive_gdef (otf, &otf_gstring);
- gidx = gstring->used;
-
- if (gsub_features)
- {
- if (OTF_drive_gsub (otf, &otf_gstring, script, langsys, gsub_features)
- < 0)
- goto simple_copy;
- for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++, otfg++)
- {
- MGlyph temp = *(MGLYPH (from + otfg->f.index.from));
-
- temp.c = otfg->c;
- temp.combining_code = 0;
- if (otfg->glyph_id)
- {
- temp.code = otfg->glyph_id;
- temp.otf_encoded = 1;
- }
- else
- {
- temp.code = temp.c;
- temp.otf_encoded = 0;
- }
- temp.to = MGLYPH (from + otfg->f.index.to)->to;
- MLIST_APPEND1 (gstring, glyphs, temp, MERROR_FONT_OTF);
- }
- }
- 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);
- }
-
- (rfont->driver->find_metric) (rfont, gstring, gidx, gstring->used);
-
- if (gpos_features)
- {
- int u;
- int size10, size;
- MGlyph *base = NULL, *mark = NULL;
-
- if (OTF_check_features (otf, 1,
- cap->script_tag, cap->langsys_tag,
- cap->features[MFONT_OTT_GPOS].tags,
- cap->features[MFONT_OTT_GPOS].nfeatures) != 1
- || (OTF_drive_gpos (otf, &otf_gstring, script, langsys,
- gpos_features) < 0))
- return to;
-
- u = otf->head->unitsPerEm;
- size10 = rfont->spec.size;
- size = size10 / 10;
-
- for (i = 0, otfg = otf_gstring.glyphs, g = MGLYPH (gidx);
- i < otf_gstring.used; i++, otfg++, g++)
- {
- MGlyph *prev;
-
- if (! otfg->glyph_id)
- continue;
- switch (otfg->positioning_type)
- {
- case 0:
- break;
- case 1: case 2:
- {
- int format = otfg->f.f1.format;
-
- if (format & OTF_XPlacement)
- g->xoff = otfg->f.f1.value->XPlacement * size10 / u / 10;
- if (format & OTF_XPlaDevice)
- g->xoff += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, size);
- if (format & OTF_YPlacement)
- g->yoff = - (otfg->f.f1.value->YPlacement * size10 / u / 10);
- if (format & OTF_YPlaDevice)
- g->yoff -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, size);
- if (format & OTF_XAdvance)
- g->width += otfg->f.f1.value->XAdvance * size10 / u / 10;
- if (format & OTF_XAdvDevice)
- g->width += DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, size);
- }
- break;
- case 3:
- /* Not yet supported. */
- break;
- case 4: case 5:
- if (! base)
- break;
- prev = base;
- goto label_adjust_anchor;
- default: /* i.e. case 6 */
- if (! mark)
- break;
- prev = mark;
-
- label_adjust_anchor:
- {
- int base_x, base_y, mark_x, mark_y;
-
- 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;
-
- if (otfg->f.f4.base_anchor->AnchorFormat != 1)
- adjust_anchor (otfg->f.f4.base_anchor, rfont->fontp,
- prev->code, size, &base_x, &base_y);
- if (otfg->f.f4.mark_anchor->AnchorFormat != 1)
- adjust_anchor (otfg->f.f4.mark_anchor, rfont->fontp,
- 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 ();
- }
- }
- if (otfg->GlyphClass == OTF_GlyphClass0)
- base = mark = g;
- else if (otfg->GlyphClass == OTF_GlyphClassMark)
- mark = g;
- else
- base = g;
- }
- }
- free (otf_gstring.glyphs);
- 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);
- }
- if (otf_gstring.glyphs)
- free (otf_gstring.glyphs);
- return to;
-}
-
-
-int
-mfont__ft_decode_otf (MGlyph *g)
-{
- MFontFT *ft_info = (MFontFT *) g->rface->rfont->font;
- int c = OTF_get_unicode (ft_info->otf, (OTF_GlyphID) g->code);
-
- return (c ? c : -1);
-}
-
-#endif /* HAVE_OTF */
-
#endif /* HAVE_FREETYPE */