]> granicus.if.org Git - libass/commitdiff
Custom font matching and font sources
authorGrigori Goronzy <greg@chown.ath.cx>
Sun, 7 Aug 2011 00:21:09 +0000 (02:21 +0200)
committerGrigori Goronzy <greg@chown.ath.cx>
Fri, 10 Jul 2015 08:41:01 +0000 (10:41 +0200)
Implement a simple font sorter (FontSelector) and an interface to deal
with multiple font sources (FontProvider). Unfinished business,
but works for the most part. Currently the only implemented FontProvider
uses fontconfig.

13 files changed:
libass/Makefile.am
libass/ass_cache.c
libass/ass_font.c
libass/ass_font.h
libass/ass_fontconfig.c
libass/ass_fontconfig.h
libass/ass_fontselect.c [new file with mode: 0644]
libass/ass_fontselect.h [new file with mode: 0644]
libass/ass_parse.c
libass/ass_render.c
libass/ass_render.h
libass/ass_render_api.c
libass/ass_shaper.c

index 4bf9584eda6b14be40e918e513e13eebbc4dd35a..b8659e6b0565a0522963123caa8b4c8d5d91e364 100644 (file)
@@ -20,14 +20,13 @@ SRC_INTEL_RASTERIZER = x86/rasterizer.asm
 SRC_RASTERIZER = ass_rasterizer.h ass_rasterizer.c ass_rasterizer_c.c
 
 lib_LTLIBRARIES = libass.la
-libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontconfig.c ass_render.c \
+libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontselect.c ass_render.c \
                     ass_utils.c ass_bitmap.c ass_blur.c ass_library.c ass_bitmap.h \
-                    ass_cache.h ass_fontconfig.h ass_font.h ass.h \
+                    ass_cache.h ass_fontselect.h ass_font.h ass.h \
                     ass_library.h ass_types.h ass_utils.h ass_drawing.c \
                     ass_drawing.h ass_cache_template.h ass_render.h \
                     ass_parse.c ass_parse.h ass_render_api.c ass_shaper.c \
-                    ass_shaper.h ass_strtod.c ass_func_template.h
-
+                    ass_shaper.h ass_strtod.c ass_fontconfig.c ass_fontconfig.h
 libass_la_LDFLAGS = -no-undefined -version-info $(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE)
 libass_la_LDFLAGS += -export-symbols $(srcdir)/libass.sym
 
index 2381e88c32dde624c980f0f0c374a9872bb20e22..d1aaec0949097fcad42cf563086e18b70f29579b 100644 (file)
@@ -43,8 +43,6 @@ static unsigned font_hash(void *buf, size_t len)
     hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
     hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
     hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval);
-    hval = fnv_32a_buf(&desc->treat_family_as_pattern,
-            sizeof(desc->treat_family_as_pattern), hval);
     hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval);
     return hval;
 }
@@ -59,8 +57,6 @@ static unsigned font_compare(void *key1, void *key2, size_t key_size)
         return 0;
     if (a->italic != b->italic)
         return 0;
-    if (a->treat_family_as_pattern != b->treat_family_as_pattern)
-        return 0;
     if (a->vertical != b->vertical)
         return 0;
     return 1;
index 5a2645efb6a218714356df2405e0ecd369eeed26..13ec654929d0b2a39909886d9af7658fe017ae73 100644 (file)
@@ -31,7 +31,7 @@
 #include "ass.h"
 #include "ass_library.h"
 #include "ass_font.h"
-#include "ass_fontconfig.h"
+#include "ass_fontselect.h"
 #include "ass_utils.h"
 #include "ass_shaper.h"
 
@@ -127,7 +127,7 @@ static void buggy_font_workaround(FT_Face face)
  * \brief Select a face with the given charcode and add it to ASS_Font
  * \return index of the new face in font->faces, -1 if failed
  */
-static int add_face(void *fc_priv, ASS_Font *font, uint32_t ch)
+static int add_face(ASS_FontSelector *fontsel, ASS_Font *font, uint32_t ch)
 {
     char *path;
     int index;
@@ -138,10 +138,9 @@ static int add_face(void *fc_priv, ASS_Font *font, uint32_t ch)
     if (font->n_faces == ASS_FONT_MAX_FACES)
         return -1;
 
-    path =
-        fontconfig_select(font->library, fc_priv, font->desc.family,
-                          font->desc.treat_family_as_pattern,
-                          font->desc.bold, font->desc.italic, &index, ch);
+    path = ass_font_select(fontsel, font->library, font->desc.family,
+            font->desc.bold, font->desc.italic, &index, ch);
+
     if (!path)
         return -1;
 
@@ -181,7 +180,7 @@ static int add_face(void *fc_priv, ASS_Font *font, uint32_t ch)
  * \brief Create a new ASS_Font according to "desc" argument
  */
 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
-                       FT_Library ftlibrary, void *fc_priv,
+                       FT_Library ftlibrary, ASS_FontSelector *fontsel,
                        ASS_FontDesc *desc)
 {
     int error;
@@ -197,7 +196,6 @@ ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
     font.shaper_priv = NULL;
     font.n_faces = 0;
     font.desc.family = strdup(desc->family);
-    font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
     font.desc.bold = desc->bold;
     font.desc.italic = desc->italic;
     font.desc.vertical = desc->vertical;
@@ -206,7 +204,7 @@ ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
     font.v.x = font.v.y = 0;
     font.size = 0.;
 
-    error = add_face(fc_priv, &font, 0);
+    error = add_face(fontsel, &font, 0);
     if (error == -1) {
         free(font.desc.family);
         return 0;
@@ -484,8 +482,8 @@ static void ass_glyph_embolden(FT_GlyphSlot slot)
  * Finds a face that has the requested codepoint and returns both face
  * and glyph index.
  */
-int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
-                       int *face_index, int *glyph_index)
+int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font,
+                       uint32_t symbol, int *face_index, int *glyph_index)
 {
     int index = 0;
     int i;
@@ -519,14 +517,13 @@ int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
             *face_index = i;
     }
 
-#ifdef CONFIG_FONTCONFIG
     if (index == 0) {
         int face_idx;
         ass_msg(font->library, MSGL_INFO,
                 "Glyph 0x%X not found, selecting one more "
                 "font for (%s, %d, %d)", symbol, font->desc.family,
                 font->desc.bold, font->desc.italic);
-        face_idx = *face_index = add_face(fcpriv, font, symbol);
+        face_idx = *face_index = add_face(fontsel, font, symbol);
         if (face_idx >= 0) {
             face = font->faces[face_idx];
             index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol));
@@ -547,7 +544,7 @@ int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
             }
         }
     }
-#endif
+
     // FIXME: make sure we have a valid face_index. this is a HACK.
     *face_index  = FFMAX(*face_index, 0);
     *glyph_index = index;
@@ -559,9 +556,8 @@ int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
  * \brief Get a glyph
  * \param ch character code
  **/
-FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
-                            uint32_t ch, int face_index, int index,
-                            ASS_Hinting hinting, int deco)
+FT_Glyph ass_font_get_glyph(ASS_Font *font, uint32_t ch, int face_index,
+                            int index, ASS_Hinting hinting, int deco)
 {
     int error;
     FT_Glyph glyph;
index f3c3f8ebdd4617c3de20934e5bac47af3ec651ac..9ccc83a2e7d42cebbddf7c4b679008dfa8d91999 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "ass.h"
 #include "ass_types.h"
+#include "ass_fontselect.h"
 
 #define VERTICAL_LOWER_BOUND 0x02f1
 
@@ -39,7 +40,6 @@ typedef struct {
     char *family;
     unsigned bold;
     unsigned italic;
-    int treat_family_as_pattern;
     int vertical;               // @font vertical layout
 } ASS_FontDesc;
 
@@ -58,7 +58,7 @@ typedef struct {
 #include "ass_cache.h"
 
 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
-                       FT_Library ftlibrary, void *fc_priv,
+                       FT_Library ftlibrary, ASS_FontSelector *fontsel,
                        ASS_FontDesc *desc);
 void ass_font_set_transform(ASS_Font *font, double scale_x,
                             double scale_y, FT_Vector *v);
@@ -66,10 +66,10 @@ void ass_face_set_size(FT_Face face, double size);
 void ass_font_set_size(ASS_Font *font, double size);
 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc,
                            int *desc);
-int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
-                       int *face_index, int *glyph_index);
+int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font,
+                       uint32_t symbol, int *face_index, int *glyph_index);
 uint32_t ass_font_index_magic(FT_Face face, uint32_t symbol);
-FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
+FT_Glyph ass_font_get_glyph(ASS_Font *font,
                             uint32_t ch, int face_index, int index,
                             ASS_Hinting hinting, int deco);
 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2);
index f0e9000ef4be2d6e563bbb9a0f2d5ea2fb7bada8..c65cd209f3ee2a1974707777e44de0f39419d6e9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
  *
  * This file is part of libass.
  *
 
 #include "config.h"
 
-#include <stdlib.h>
+#ifdef CONFIG_FONTCONFIG
+
 #include <stdio.h>
-#include <assert.h>
-#include <string.h>
-#include <strings.h>
+#include <stdlib.h>
 #include <sys/types.h>
-#include <sys/stat.h>
-#include <inttypes.h>
-#include <ft2build.h>
-#include FT_FREETYPE_H
 
-#include "ass_utils.h"
-#include "ass.h"
-#include "ass_library.h"
-#include "ass_fontconfig.h"
-
-#ifdef CONFIG_FONTCONFIG
 #include <fontconfig/fontconfig.h>
 #include <fontconfig/fcfreetype.h>
-#endif
-
-struct fc_instance {
-#ifdef CONFIG_FONTCONFIG
-    FcConfig *config;
-#endif
-    char *family_default;
-    char *path_default;
-    int index_default;
-};
-
-#ifdef CONFIG_FONTCONFIG
 
-/**
- * \brief Case-insensitive match ASS/SSA font family against full name. (also
- * known as "name for humans")
- *
- * \param lib library instance
- * \param priv fontconfig instance
- * \param family font fullname
- * \param bold weight attribute
- * \param italic italic attribute
- * \return font set
- */
-static FcFontSet *
-match_fullname(ASS_Library *lib, FCInstance *priv, const char *family,
-               unsigned bold, unsigned italic)
-{
-    FcFontSet *sets[2];
-    FcFontSet *result = FcFontSetCreate();
-    int nsets = 0;
-    int i, fi;
-
-    if (!result)
-        return NULL;
-
-    if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetSystem)))
-        nsets++;
-    if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetApplication)))
-        nsets++;
-
-    // Run over font sets and patterns and try to match against full name
-    for (i = 0; i < nsets; i++) {
-        FcFontSet *set = sets[i];
-        for (fi = 0; fi < set->nfont; fi++) {
-            FcPattern *pat = set->fonts[fi];
-            char *fullname;
-            int pi = 0, at;
-            FcBool ol;
-            while (FcPatternGetString(pat, FC_FULLNAME, pi++,
-                   (FcChar8 **) &fullname) == FcResultMatch) {
-                if (FcPatternGetBool(pat, FC_OUTLINE, 0, &ol) != FcResultMatch
-                    || ol != FcTrue)
-                    continue;
-                if (FcPatternGetInteger(pat, FC_SLANT, 0, &at) != FcResultMatch
-                    || at < italic)
-                    continue;
-                if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &at) != FcResultMatch
-                    || at < bold)
-                    continue;
-                if (strcasecmp(fullname, family) == 0) {
-                    FcFontSetAdd(result, FcPatternDuplicate(pat));
-                    break;
-                }
-            }
-        }
-    }
+#include "ass_fontconfig.h"
+#include "ass_fontselect.h"
+#include "ass_utils.h"
 
-    return result;
-}
+#define MAX_FULLNAME 100
 
-/**
- * \brief Low-level font selection.
- * \param priv private data
- * \param family font family
- * \param treat_family_as_pattern treat family as fontconfig pattern
- * \param bold font weight value
- * \param italic font slant value
- * \param index out: font index inside a file
- * \param code: the character that should be present in the font, can be 0
- * \return font file path
-*/
-static char *select_font(ASS_Library *library, FCInstance *priv,
-                          const char *family, int treat_family_as_pattern,
-                          unsigned bold, unsigned italic, int *index,
-                          uint32_t code)
+static int check_glyph(void *priv, uint32_t code)
 {
-    FcBool rc;
-    FcResult result;
-    FcPattern *pat = NULL, *rpat = NULL;
-    int r_index, r_slant, r_weight;
-    FcChar8 *r_family, *r_style, *r_file, *r_fullname;
-    FcBool r_outline, r_embolden;
-    FcCharSet *r_charset;
-    FcFontSet *ffullname = NULL, *fsorted = NULL, *fset = NULL;
-    int curf;
-    char *retval = NULL;
-    int family_cnt = 0;
-
-    *index = 0;
-
-    if (treat_family_as_pattern)
-        pat = FcNameParse((const FcChar8 *) family);
-    else
-        pat = FcPatternCreate();
+    FcPattern *pat = (FcPattern *)priv;
+    FcCharSet *charset;
 
     if (!pat)
-        goto error;
-
-    if (!treat_family_as_pattern) {
-        FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) family);
+        return 1;
 
-        // In SSA/ASS fonts are sometimes referenced by their "full name",
-        // which is usually a concatenation of family name and font
-        // style (ex. Ottawa Bold). Full name is available from
-        // FontConfig pattern element FC_FULLNAME, but it is never
-        // used for font matching.
-        // Therefore, I'm removing words from the end of the name one
-        // by one, and adding shortened names to the pattern. It seems
-        // that the first value (full name in this case) has
-        // precedence in matching.
-        // An alternative approach could be to reimplement FcFontSort
-        // using FC_FULLNAME instead of FC_FAMILY.
-        family_cnt = 1;
-        {
-            char *s = strdup(family);
-            if (!s)
-                goto error;
-            char *p = s + strlen(s);
-            while (--p > s)
-                if (*p == ' ' || *p == '-') {
-                    *p = '\0';
-                    FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) s);
-                    ++family_cnt;
-                }
-            free(s);
-        }
-    }
-    FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
-    FcPatternAddInteger(pat, FC_SLANT, italic);
-    FcPatternAddInteger(pat, FC_WEIGHT, bold);
-
-    FcDefaultSubstitute(pat);
-
-    rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern);
-    if (!rc)
-        goto error;
-    /* Fontconfig defaults include a language setting, which it sets based on
-     * some environment variables or defaults to "en". Unset this as we don't
-     * know the real language, and because some some attached fonts lack
-     * non-ascii characters included in fontconfig's list of characters
-     * required for English support and therefore don't match the lang=en
-     * criterion.
-     */
-    FcPatternDel(pat, "lang");
-
-    fsorted = FcFontSort(priv->config, pat, FcFalse, NULL, &result);
-    ffullname = match_fullname(library, priv, family, bold, italic);
-    if (!fsorted || !ffullname)
-        goto error;
-
-    fset = FcFontSetCreate();
-    for (curf = 0; curf < ffullname->nfont; ++curf) {
-        FcPattern *curp = ffullname->fonts[curf];
-        FcPatternReference(curp);
-        FcFontSetAdd(fset, curp);
-    }
-    for (curf = 0; curf < fsorted->nfont; ++curf) {
-        FcPattern *curp = fsorted->fonts[curf];
-        FcPatternReference(curp);
-        FcFontSetAdd(fset, curp);
-    }
-
-    for (curf = 0; curf < fset->nfont; ++curf) {
-        FcPattern *curp = fset->fonts[curf];
-
-        result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline);
-        if (result != FcResultMatch)
-            continue;
-        if (r_outline != FcTrue)
-            continue;
-        if (!code)
-            break;
-        result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset);
-        if (result != FcResultMatch)
-            continue;
-        if (FcCharSetHasChar(r_charset, code))
-            break;
-    }
-
-    if (curf >= fset->nfont)
-        goto error;
-
-    if (!treat_family_as_pattern) {
-        // Remove all extra family names from original pattern.
-        // After this, FcFontRenderPrepare will select the most relevant family
-        // name in case there are more than one of them.
-        for (; family_cnt > 1; --family_cnt)
-            FcPatternRemove(pat, FC_FAMILY, family_cnt - 1);
-    }
-
-    rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]);
-    if (!rpat)
-        goto error;
-
-    result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index);
-    if (result != FcResultMatch)
-        goto error;
-    *index = r_index;
-
-    result = FcPatternGetString(rpat, FC_FILE, 0, &r_file);
-    if (result != FcResultMatch)
-        goto error;
-    retval = strdup((const char *) r_file);
-    if (!retval)
-        goto error;
-
-    result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family);
-    if (result != FcResultMatch)
-        r_family = NULL;
-
-    result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname);
-    if (result != FcResultMatch)
-        r_fullname = NULL;
-
-    if (!treat_family_as_pattern &&
-        !(r_family && strcasecmp((const char *) r_family, family) == 0) &&
-        !(r_fullname && strcasecmp((const char *) r_fullname, family) == 0)) {
-        char *fallback = (char *) (r_fullname ? r_fullname : r_family);
-        if (code) {
-            ass_msg(library, MSGL_WARN,
-                    "fontconfig: cannot find glyph U+%04X in font '%s', falling back to '%s'",
-                    (unsigned int)code, family, fallback);
-        } else {
-            ass_msg(library, MSGL_WARN,
-                    "fontconfig: cannot find font '%s', falling back to '%s'",
-                    family, fallback);
-        }
-    }
+    if (code == 0)
+        return 1;
 
-    result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style);
+    FcResult result = FcPatternGetCharSet(pat, FC_CHARSET, 0, &charset);
     if (result != FcResultMatch)
-        r_style = NULL;
-
-    result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant);
-    if (result != FcResultMatch)
-        r_slant = 0;
-
-    result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight);
-    if (result != FcResultMatch)
-        r_weight = 0;
-
-    result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden);
-    if (result != FcResultMatch)
-        r_embolden = 0;
-
-    ass_msg(library, MSGL_V,
-           "Font info: family '%s', style '%s', fullname '%s',"
-           " slant %d, weight %d%s", (const char *) r_family,
-           (const char *) r_style, (const char *) r_fullname, r_slant,
-           r_weight, r_embolden ? ", embolden" : "");
-
-  error:
-    if (pat)
-        FcPatternDestroy(pat);
-    if (rpat)
-        FcPatternDestroy(rpat);
-    if (fsorted)
-        FcFontSetDestroy(fsorted);
-    if (ffullname)
-        FcFontSetDestroy(ffullname);
-    if (fset)
-        FcFontSetDestroy(fset);
-    return retval;
+        return 0;
+    if (FcCharSetHasChar(charset, code) == FcTrue)
+        return 1;
+    return 0;
 }
 
-/**
- * \brief Find a font. Use default family or path if necessary.
- * \param priv_ private data
- * \param family font family
- * \param treat_family_as_pattern treat family as fontconfig pattern
- * \param bold font weight value
- * \param italic font slant value
- * \param index out: font index inside a file
- * \param code: the character that should be present in the font, can be 0
- * \return font file path
-*/
-char *fontconfig_select(ASS_Library *library, FCInstance *priv,
-                        const char *family, int treat_family_as_pattern,
-                        unsigned bold, unsigned italic, int *index,
-                        uint32_t code)
+static void destroy(void *priv)
 {
-    char *res = 0;
-    if (!priv->config) {
-        *index = priv->index_default;
-        res = priv->path_default ? strdup(priv->path_default) : 0;
-        return res;
-    }
-    if (family && *family)
-        res =
-            select_font(library, priv, family, treat_family_as_pattern,
-                         bold, italic, index, code);
-    if (!res && priv->family_default) {
-        res =
-            select_font(library, priv, priv->family_default, 0, bold,
-                         italic, index, code);
-        if (res)
-            ass_msg(library, MSGL_WARN, "fontconfig_select: Using default "
-                    "font family: (%s, %d, %d) -> %s, %d",
-                    family, bold, italic, res, *index);
-    }
-    if (!res && priv->path_default) {
-        res = strdup(priv->path_default);
-        *index = priv->index_default;
-        if (res)
-            ass_msg(library, MSGL_WARN, "fontconfig_select: Using default font: "
-                    "(%s, %d, %d) -> %s, %d", family, bold, italic,
-                    res, *index);
-    }
-    if (!res) {
-        res = select_font(library, priv, "Arial", 0, bold, italic,
-                           index, code);
-        if (res)
-            ass_msg(library, MSGL_WARN, "fontconfig_select: Using 'Arial' "
-                    "font family: (%s, %d, %d) -> %s, %d", family, bold,
-                    italic, res, *index);
-    }
-    if (res)
-        ass_msg(library, MSGL_V,
-                "fontconfig_select: (%s, %d, %d) -> %s, %d", family, bold,
-                italic, res, *index);
-    return res;
+    FcConfig *config = (FcConfig *)priv;
+    FcConfigDestroy(config);
 }
 
-/**
- * \brief Process memory font.
- * \param priv private data
- * \param library library object
- * \param ftlibrary freetype library object
- * \param idx index of the processed font in library->fontdata
- *
- * Builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
-*/
-static void process_fontdata(FCInstance *priv, ASS_Library *library,
-                             FT_Library ftlibrary, int idx)
+static void scan_fonts(FcConfig *config, ASS_FontProvider *provider)
 {
-    int rc;
-    const char *name = library->fontdata[idx].name;
-    const char *data = library->fontdata[idx].data;
-    int data_size = library->fontdata[idx].size;
-
-    FT_Face face;
-    FcPattern *pattern;
-    FcFontSet *fset;
-    FcBool res;
-    int face_index, num_faces = 1;
-
-    for (face_index = 0; face_index < num_faces; ++face_index) {
-        ass_msg(library, MSGL_V, "Adding memory font '%s'", name);
+    int i;
+    FcFontSet *fonts;
+    ASS_FontProviderMetaData meta;
+
+    // get list of fonts
+    fonts = FcConfigGetFonts(config, FcSetSystem);
+
+    // fill font_info list
+    for (i = 0; i < fonts->nfont; i++) {
+        FcPattern *pat = fonts->fonts[i];
+        FcBool outline;
+        int index;
+        char *path;
+        char *fullnames[MAX_FULLNAME];
+
+        // skip non-outline fonts
+        FcResult result = FcPatternGetBool(pat, FC_OUTLINE, 0, &outline);
+        if (result != FcResultMatch || outline != FcTrue)
+            continue;
 
-        rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data,
-                                data_size, face_index, &face);
-        if (rc) {
-            ass_msg(library, MSGL_WARN, "Error opening memory font: %s",
-                   name);
-            return;
-        }
-        num_faces = face->num_faces;
+        // simple types
+        result  = FcPatternGetInteger(pat, FC_SLANT, 0, &meta.slant);
+        result |= FcPatternGetInteger(pat, FC_WEIGHT, 0, &meta.weight);
+        result |= FcPatternGetInteger(pat, FC_INDEX, 0, &index);
+        if (result != FcResultMatch)
+            continue;
 
-        pattern =
-            FcFreeTypeQueryFace(face, (unsigned char *) name, face_index,
-                                FcConfigGetBlanks(priv->config));
-        if (!pattern) {
-            ass_msg(library, MSGL_WARN, "%s failed", "FcFreeTypeQueryFace");
-            FT_Done_Face(face);
-            return;
-        }
+        // family name
+        // HACK: get the last family name. that fixes fonts
+        // like Arial Narrow in some versions
+        int n_family = 0;
+        while (FcPatternGetString(pat, FC_FAMILY, n_family,
+                    (FcChar8 **)&meta.family) == FcResultMatch)
+            n_family++;
 
-        fset = FcConfigGetFonts(priv->config, FcSetSystem);     // somehow it failes when asked for FcSetApplication
-        if (!fset) {
-            ass_msg(library, MSGL_WARN, "%s failed", "FcConfigGetFonts");
-            FT_Done_Face(face);
-            return;
-        }
+        // path
+        result = FcPatternGetString(pat, FC_FILE, 0, (FcChar8 **)&path);
+        if (result != FcResultMatch)
+            continue;
 
-        res = FcFontSetAdd(fset, pattern);
-        if (!res) {
-            ass_msg(library, MSGL_WARN, "%s failed", "FcFontSetAdd");
-            FT_Done_Face(face);
-            return;
-        }
+        // read and strdup fullnames
+        meta.n_fullname = 0;
+        while (FcPatternGetString(pat, FC_FULLNAME, meta.n_fullname,
+                    (FcChar8 **)&fullnames[meta.n_fullname]) == FcResultMatch
+                    && meta.n_fullname < MAX_FULLNAME)
+            meta.n_fullname++;
+        meta.fullnames = fullnames;
 
-        FT_Done_Face(face);
+        ass_font_provider_add_font(provider, &meta, path, index, (void *)pat);
     }
 }
 
-/**
- * \brief Init fontconfig.
- * \param library libass library object
- * \param ftlibrary freetype library object
- * \param family default font family
- * \param path default font path
- * \param fc whether fontconfig should be used
- * \param config path to a fontconfig configuration file, or NULL
- * \param update whether the fontconfig cache should be built/updated
- * \return pointer to fontconfig private data
-*/
-FCInstance *fontconfig_init(ASS_Library *library,
-                            FT_Library ftlibrary, const char *family,
-                            const char *path, int fc, const char *config,
-                            int update)
+static ASS_FontProviderFuncs fontconfig_callbacks = {
+    NULL,
+    check_glyph,
+    NULL,
+    destroy
+};
+
+ASS_FontProvider *
+ass_fontconfig_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
+                            const char *config)
 {
     int rc;
-    FCInstance *priv = calloc(1, sizeof(FCInstance));
-    const char *dir = library->fonts_dir;
-    int i;
-
-    if (!priv)
-        return NULL;
-
-    if (!fc) {
-        ass_msg(library, MSGL_WARN,
-               "Fontconfig disabled, only default font will be used.");
-        goto exit;
-    }
+    FcConfig *fc_config;
+    ASS_FontProvider *provider = NULL;
 
-    priv->config = FcConfigCreate();
-    rc = FcConfigParseAndLoad(priv->config, (unsigned char *) config, FcTrue);
+    // build and load fontconfig configuration
+    fc_config = FcConfigCreate();
+    rc = FcConfigParseAndLoad(fc_config, (unsigned char *) config, FcTrue);
     if (!rc) {
-        ass_msg(library, MSGL_WARN, "No usable fontconfig configuration "
+        ass_msg(lib, MSGL_WARN, "No usable fontconfig configuration "
                 "file found, using fallback.");
-        FcConfigDestroy(priv->config);
-        priv->config = FcInitLoadConfig();
+        FcConfigDestroy(fc_config);
+        fc_config = FcInitLoadConfig();
         rc++;
     }
-    if (rc && update) {
-        FcConfigBuildFonts(priv->config);
-    }
+    if (rc)
+        FcConfigBuildFonts(fc_config);
 
-    if (!rc || !priv->config) {
-        ass_msg(library, MSGL_FATAL,
+    if (!rc || !fc_config) {
+        ass_msg(lib, MSGL_FATAL,
                 "No valid fontconfig configuration found!");
-        FcConfigDestroy(priv->config);
+        FcConfigDestroy(fc_config);
         goto exit;
     }
 
-    for (i = 0; i < library->num_fontdata; ++i)
-        process_fontdata(priv, library, ftlibrary, i);
+    // create font provider
+    provider = ass_font_provider_new(selector, &fontconfig_callbacks,
+            (void *)fc_config);
 
-    if (dir) {
-        ass_msg(library, MSGL_V, "Updating font cache");
-
-        rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir);
-        if (!rc) {
-            ass_msg(library, MSGL_WARN, "%s failed", "FcConfigAppFontAddDir");
-        }
-    }
+    // scan fonts
+    scan_fonts(fc_config, provider);
 
-    priv->family_default = family ? strdup(family) : NULL;
 exit:
-    priv->path_default = path ? strdup(path) : NULL;
-    priv->index_default = 0;
-
-    return priv;
-}
-
-int fontconfig_update(FCInstance *priv)
-{
-        return FcConfigBuildFonts(priv->config);
-}
-
-#else                           /* CONFIG_FONTCONFIG */
-
-char *fontconfig_select(ASS_Library *library, FCInstance *priv,
-                        const char *family, int treat_family_as_pattern,
-                        unsigned bold, unsigned italic, int *index,
-                        uint32_t code)
-{
-    *index = priv->index_default;
-    char* res = priv->path_default ? strdup(priv->path_default) : 0;
-    return res;
-}
-
-FCInstance *fontconfig_init(ASS_Library *library,
-                            FT_Library ftlibrary, const char *family,
-                            const char *path, int fc, const char *config,
-                            int update)
-{
-    FCInstance *priv;
-
-    ass_msg(library, MSGL_WARN,
-        "Fontconfig disabled, only default font will be used.");
-
-    priv = calloc(1, sizeof(FCInstance));
-    if (!priv)
-        return NULL;
-
-    priv->path_default = path ? strdup(path) : 0;
-    priv->index_default = 0;
-    return priv;
-}
-
-int fontconfig_update(FCInstance *priv)
-{
-    // Do nothing
-    return 1;
+    return provider;
 }
 
 #endif
-
-void fontconfig_done(FCInstance *priv)
-{
-
-    if (priv) {
-#ifdef CONFIG_FONTCONFIG
-        if (priv->config)
-            FcConfigDestroy(priv->config);
-#endif
-        free(priv->path_default);
-        free(priv->family_default);
-    }
-    free(priv);
-}
index 396fb72d0d3c5e15dd82437d8a1c731b6bfc589f..6d4f5e9bdefc093c1a0206f8ec37ee6f2089c818 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
  *
  * This file is part of libass.
  *
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef LIBASS_FONTCONFIG_H
-#define LIBASS_FONTCONFIG_H
+#include "config.h"
 
-#include <stdint.h>
 #include "ass_types.h"
-#include "ass.h"
-#include <ft2build.h>
-#include FT_FREETYPE_H
+#include "ass_fontselect.h"
+
+#ifndef ASS_FONTCONFIG_H
+#define ASS_FONTCONFIG_H
 
 #ifdef CONFIG_FONTCONFIG
-#include <fontconfig/fontconfig.h>
-#endif
 
-typedef struct fc_instance FCInstance;
+ASS_FontProvider *
+ass_fontconfig_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
+                            const char *config);
 
-FCInstance *fontconfig_init(ASS_Library *library,
-                            FT_Library ftlibrary, const char *family,
-                            const char *path, int fc, const char *config,
-                            int update);
-char *fontconfig_select(ASS_Library *library, FCInstance *priv,
-                        const char *family, int treat_family_as_pattern,
-                        unsigned bold, unsigned italic, int *index,
-                        uint32_t code);
-void fontconfig_done(FCInstance *priv);
-int fontconfig_update(FCInstance *priv);
+#endif
 
-#endif                          /* LIBASS_FONTCONFIG_H */
+#endif
diff --git a/libass/ass_fontselect.c b/libass/ass_fontselect.c
new file mode 100644 (file)
index 0000000..481d4c9
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_SFNT_NAMES_H
+#include <iconv.h>
+
+#include "ass_utils.h"
+#include "ass.h"
+#include "ass_library.h"
+#include "ass_fontselect.h"
+#include "ass_fontconfig.h"
+
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#define MAX_FULLNAME 100
+
+// proposed structure for holding font data, used for collection
+// and matching. strings are utf-8.
+struct font_info {
+    char *family;       // family name
+    char **fullnames;   // list of localized fullnames (e.g. Arial Bold Italic)
+    int n_fullname;
+
+    int slant;
+    int weight;
+
+    // how to access this face
+    char *path;
+    int index;
+
+    // similarity score
+    unsigned score;
+
+    // callbacks
+    ASS_FontProviderFuncs funcs;
+
+    // private data for callbacks
+    void *priv;
+};
+
+struct font_selector {
+    // fallbacks
+    char *family_default;
+    char *path_default;
+    int index_default;
+
+    // font database
+    int n_font;
+    int alloc_font;
+    ASS_FontInfo *font_infos;
+
+    // XXX: for now, manage a single provider
+    ASS_FontProvider *provider;
+};
+
+struct font_provider {
+    ASS_FontSelector *parent;
+    ASS_FontProviderFuncs funcs;
+    void *priv;
+};
+
+ASS_FontProvider *
+ass_font_provider_new(ASS_FontSelector *selector, ASS_FontProviderFuncs *funcs,
+                      void *priv)
+{
+    ASS_FontProvider *provider = calloc(1, sizeof(ASS_FontProvider));
+
+    provider->parent   = selector;
+    provider->funcs    = *funcs;
+    provider->priv     = priv;
+
+    return provider;
+}
+
+int
+ass_font_provider_add_font(ASS_FontProvider *provider,
+                           ASS_FontProviderMetaData *meta, const char *path,
+                           unsigned int index, void *data)
+{
+    int i;
+    ASS_FontSelector *selector = provider->parent;
+    ASS_FontInfo *info;
+
+    // TODO: sanity checks. do we have a path or valid get_face function?
+
+    // check size
+    if (selector->n_font >= selector->alloc_font) {
+        selector->alloc_font = FFMAX(1, 2 * selector->alloc_font);
+        selector->font_infos = realloc(selector->font_infos,
+                selector->alloc_font * sizeof(ASS_FontInfo));
+    }
+
+    // copy over metadata
+    info = selector->font_infos + selector->n_font;
+    memset(info, 0, sizeof(ASS_FontInfo));
+
+    info->slant       = meta->slant;
+    info->weight      = meta->weight;
+    info->family      = strdup(meta->family);
+    info->n_fullname  = meta->n_fullname;
+    info->fullnames   = calloc(meta->n_fullname, sizeof(char *));
+
+    for (i = 0; i < info->n_fullname; i++)
+        info->fullnames[i] = strdup(meta->fullnames[i]);
+
+    if (path)
+        info->path = strdup(path);
+
+    info->index = index;
+    info->priv  = data;
+    info->funcs = provider->funcs;
+
+    selector->n_font++;
+
+    return 1;
+}
+
+void ass_font_provider_free(ASS_FontProvider *provider)
+{
+    // TODO: this should probably remove all fonts that belong
+    // to this provider from the list
+
+    if (provider->funcs.destroy_provider)
+        provider->funcs.destroy_provider(provider->priv);
+    free(provider);
+}
+
+
+
+/**
+ * \brief Compare a font (a) against a font request (b). Records
+ * a matching score - the lower the better.
+ * \param a font
+ * \param b font request
+ * \return matching score
+ */
+static unsigned font_info_similarity(ASS_FontInfo *a, ASS_FontInfo *b)
+{
+    int i, j;
+    unsigned similarity = 0;
+
+    // compare fullnames
+    // a matching fullname is very nice and instantly drops the score to zero
+    similarity = 10000;
+    for (i = 0; i < a->n_fullname; i++)
+        for (j = 0; j < b->n_fullname; j++) {
+            if (ABS(strcasecmp(a->fullnames[i], b->fullnames[j])) == 0)
+                similarity = 0;
+        }
+
+    // if we don't have any match, compare fullnames against family
+    // sometimes the family name is used similarly
+    if (similarity > 0) {
+        for (i = 0; i < b->n_fullname; i++) {
+            if (ABS(strcasecmp(a->family, b->fullnames[i])) == 0)
+                similarity = 0;
+        }
+    }
+
+    // compare shortened family, if no fullname matches
+    if (similarity > 0 && ABS(strcasecmp(a->family, b->family)) == 0)
+        similarity = 1000;
+
+    // nothing found? Try fallback fonts
+    // XXX: add more than arial
+    if (similarity > 1000 && ABS(strcasecmp(a->family, "Arial")) == 0)
+        similarity = 2000;
+
+    // compare slant
+    similarity += ABS(a->slant - b->slant);
+
+    // compare weight
+    similarity += ABS(a->weight - b->weight);
+
+    return similarity;
+}
+
+// calculate scores
+static void font_info_req_similarity(ASS_FontInfo *font_infos, size_t len,
+                                     ASS_FontInfo *req)
+{
+    int i;
+
+    for (i = 0; i < len; i++)
+        font_infos[i].score = font_info_similarity(&font_infos[i], req);
+}
+
+#if 1
+// dump font information
+static void font_info_dump(ASS_FontInfo *font_infos, size_t len)
+{
+    int i, j;
+
+    // dump font infos
+    for (i = 0; i < len; i++) {
+        printf("font %d\n", i);
+        printf("  family: '%s'\n", font_infos[i].family);
+        printf("  fullnames: ");
+        for (j = 0; j < font_infos[i].n_fullname; j++)
+            printf("'%s' ", font_infos[i].fullnames[j]);
+        printf("\n");
+        printf("  slant: %d\n", font_infos[i].slant);
+        printf("  weight: %d\n", font_infos[i].weight);
+        printf("  path: %s\n", font_infos[i].path);
+        printf("  index: %d\n", font_infos[i].index);
+        printf("  score: %d\n", font_infos[i].score);
+
+    }
+}
+#endif
+
+static int font_info_compare(const void *av, const void *bv)
+{
+    const ASS_FontInfo *a = av;
+    const ASS_FontInfo *b = bv;
+
+    return a->score - b->score;
+}
+
+static char *select_font(ASS_FontSelector *priv, ASS_Library *library,
+                          const char *family, unsigned bold,
+                          unsigned italic, int *index, uint32_t code)
+{
+    int num_fonts = priv->n_font;
+    ASS_FontInfo *font_infos = priv->font_infos;
+    ASS_FontInfo req;
+    char *req_fullname;
+
+    // do we actually have any fonts?
+    if (!priv->n_font)
+        return NULL;
+
+    // fill font request
+    memset(&req, 0, sizeof(ASS_FontInfo));
+    req.slant   = italic;
+    req.weight  = bold;
+    req.n_fullname   = 1;
+    req.fullnames    = &req_fullname;
+    req.fullnames[0] = (char *)family;
+    req.family       = strdup(family);
+    char *p = strchr(req.family, ' ');
+    if (p) *p = 0;
+
+    // calculate similarities
+    font_info_req_similarity(font_infos, num_fonts, &req);
+
+    // sort
+    qsort(font_infos, num_fonts, sizeof(ASS_FontInfo),
+            font_info_compare);
+
+    // check glyph coverage
+    int info_index = 0;
+    while (info_index < priv->n_font && font_infos[info_index].funcs.check_glyph
+            && font_infos[info_index].funcs.check_glyph(font_infos[info_index].priv, code) == 0)
+        info_index++;
+
+    free(req.family);
+
+    // return best match
+    if (!font_infos[info_index].path)
+        return NULL;
+    *index = font_infos[info_index].index;
+    return strdup(font_infos[info_index].path);
+}
+
+
+/**
+ * \brief Find a font. Use default family or path if necessary.
+ * \param library ASS library handle
+ * \param family font family
+ * \param treat_family_as_pattern treat family as fontconfig pattern
+ * \param bold font weight value
+ * \param italic font slant value
+ * \param index out: font index inside a file
+ * \param code: the character that should be present in the font, can be 0
+ * \return font file path
+*/
+char *ass_font_select(ASS_FontSelector *priv, ASS_Library *library,
+                      const char *family, unsigned bold, unsigned italic,
+                      int *index, uint32_t code)
+{
+    char *res = 0;
+
+    if (family && *family)
+        res = select_font(priv, library, family, bold, italic, index, code);
+
+    if (!res && priv->family_default) {
+        res = select_font(priv, library, priv->family_default, bold,
+                italic, index, code);
+        if (res)
+            ass_msg(library, MSGL_WARN, "fontselect: Using default "
+                    "font family: (%s, %d, %d) -> %s, %d",
+                    family, bold, italic, res, *index);
+    }
+
+    if (!res && priv->path_default) {
+        res = strdup(priv->path_default);
+        *index = priv->index_default;
+        ass_msg(library, MSGL_WARN, "fontselect: Using default font: "
+                "(%s, %d, %d) -> %s, %d", family, bold, italic,
+                res, *index);
+    }
+
+    if (!res) {
+        res = select_font(priv, library, "Arial", bold, italic,
+                           index, code);
+        if (res)
+            ass_msg(library, MSGL_WARN, "fontselect: Using 'Arial' "
+                    "font family: (%s, %d, %d) -> %s, %d", family, bold,
+                    italic, res, *index);
+    }
+
+    if (res)
+        ass_msg(library, MSGL_V,
+                "fontselect: (%s, %d, %d) -> %s, %d", family, bold,
+                italic, res, *index);
+
+    return res;
+}
+
+static int get_font_info(FT_Library lib, FT_Face face, ASS_FontInfo *info)
+{
+    int i;
+    int num_fullname = 0;
+    int num_names = FT_Get_Sfnt_Name_Count(face);
+    int slant, weight;
+    char *fullnames[100];
+    char *family = NULL;
+    iconv_t utf16to8;
+
+    // we're only interested in outlines
+    if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
+        return 0;
+
+    // scan font names
+    utf16to8 = iconv_open("UTF-8", "UTF-16BE");
+    for (i = 0; i < num_names && num_fullname < 100; i++) {
+        FT_SfntName name;
+        FT_Get_Sfnt_Name(face, i, &name);
+        //printf("name %d pid %d eid %d lid %d nameid %d\n",
+        //        i, name.platform_id, name.encoding_id, name.language_id, name.name_id);
+        // we add both full names and alternate family names to the list of full names
+        if (name.platform_id == 3 && (name.name_id == 4 || name.name_id == 1)) {
+            char buf[1024];
+            char *bufptr = buf;
+            size_t inbytes = name.string_len;
+            size_t outbytes = 1024;
+            iconv(utf16to8, (char**)&name.string, &inbytes, &bufptr, &outbytes);
+            *bufptr = '\0';
+            // no primary family name yet - just use the first we encounter as a best guess
+            if (family == NULL && name.name_id == 1) {
+                family = strdup(buf);
+                continue;
+            }
+            fullnames[num_fullname] = strdup(buf);
+            num_fullname++;
+        }
+    }
+    iconv_close(utf16to8);
+
+    // check if we got a valid family - if not use whatever FreeType gives us
+    if (family == NULL)
+        family = strdup(face->family_name);
+
+    // calculate sensible slant and weight from style attributes
+    slant  = 110 * !!(face->style_flags & FT_STYLE_FLAG_ITALIC);
+    weight = 120 * !!(face->style_flags & FT_STYLE_FLAG_BOLD) + 80;
+
+    // fill our struct
+    info->family = family;
+    info->slant  = slant;
+    info->weight = weight;
+    info->fullnames = calloc(sizeof(char *), num_fullname);
+    memcpy(info->fullnames, &fullnames, sizeof(char *) * num_fullname);
+    info->n_fullname = num_fullname;
+
+    return 1;
+}
+
+/**
+ * \brief Process memory font.
+ * \param priv private data
+ * \param library library object
+ * \param ftlibrary freetype library object
+ * \param idx index of the processed font in library->fontdata
+ *
+ * Builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
+*/
+static void process_fontdata(ASS_FontSelector *priv, ASS_Library *library,
+                             FT_Library ftlibrary, int idx)
+{
+    int rc;
+    const char *name = library->fontdata[idx].name;
+    const char *data = library->fontdata[idx].data;
+    int data_size = library->fontdata[idx].size;
+
+    FT_Face face;
+    int face_index, num_faces = 1;
+
+    for (face_index = 0; face_index < num_faces; ++face_index) {
+        rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data,
+                                data_size, face_index, &face);
+        if (rc) {
+            ass_msg(library, MSGL_WARN, "Error opening memory font: %s",
+                   name);
+            return;
+        }
+        num_faces = face->num_faces;
+
+        // get font metadata and add to list
+        ASS_FontInfo info;
+        memset(&info, 0, sizeof(ASS_FontInfo));
+        if (!get_font_info(ftlibrary, face, &info))
+            continue;
+        info.index = face_index;
+        info.path  = strdup(name);
+
+        priv->font_infos = realloc(priv->font_infos, sizeof(ASS_FontInfo) * (priv->n_font + 1));
+        memcpy(priv->font_infos + priv->n_font, &info, sizeof(ASS_FontInfo));
+        priv->n_font++;
+
+        FT_Done_Face(face);
+    }
+}
+
+/**
+ * \brief Init font selector.
+ * \param library libass library object
+ * \param ftlibrary freetype library object
+ * \param family default font family
+ * \param path default font path
+ * \return newly created font selector
+ */
+ASS_FontSelector *
+ass_fontselect_init(ASS_Library *library,
+                    FT_Library ftlibrary, const char *family,
+                    const char *path)
+{
+    int i;
+    ASS_FontSelector *priv = calloc(1, sizeof(ASS_FontSelector));
+
+    priv->family_default = family ? strdup(family) : NULL;
+    priv->path_default = path ? strdup(path) : NULL;
+    priv->index_default = 0;
+
+    // XXX: for now, always add the fontconfig provider
+    priv->provider = ass_fontconfig_add_provider(library, priv, NULL);
+
+    // XXX: use a real font provider for this
+    for (i = 0; i < library->num_fontdata; ++i)
+        process_fontdata(priv, library, ftlibrary, i);
+
+    return priv;
+}
+
+/**
+ * \brief Free font selector and release associated data
+ *
+ */
+void ass_fontselect_free(ASS_FontSelector *priv)
+{
+    int i;
+
+    if (priv) {
+        for (i = 0; i < priv->n_font; i++) {
+            ASS_FontInfo *info = priv->font_infos + i;
+            int j;
+            for (j = 0; j < info->n_fullname; j++)
+                free(info->fullnames[j]);
+            free(info->fullnames);
+            free(info->family);
+            if (info->path)
+                free(info->path);
+            if (info->funcs.destroy_font)
+                info->funcs.destroy_font(info->priv);
+        }
+        free(priv->font_infos);
+        free(priv->path_default);
+        free(priv->family_default);
+    }
+
+    // TODO: we should track all child font providers and
+    // free them here
+    ass_font_provider_free(priv->provider);
+
+    free(priv);
+}
diff --git a/libass/ass_fontselect.h b/libass/ass_fontselect.h
new file mode 100644 (file)
index 0000000..09c606b
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LIBASS_FONTCONFIG_H
+#define LIBASS_FONTCONFIG_H
+
+#include <stdint.h>
+#include "ass_types.h"
+#include "ass.h"
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+typedef struct font_selector ASS_FontSelector;
+typedef struct font_provider ASS_FontProvider;
+typedef struct font_info ASS_FontInfo;
+
+// get face data
+typedef void *(*GetFaceFunc)(void *);
+
+// check for a glyph
+typedef int (*CheckGlyphFunc)(void *, uint32_t);
+
+// destroy font_info and related data
+typedef void (*DestroyFunc)(void *);
+typedef void (*DestroyProviderFunc)(void *);
+
+typedef struct font_provider_funcs {
+    GetFaceFunc     get_face;
+    CheckGlyphFunc  check_glyph;
+    DestroyFunc     destroy_font;
+    DestroyProviderFunc destroy_provider;
+} ASS_FontProviderFuncs;
+
+typedef struct font_provider_meta_data {
+    char *family;
+    char **fullnames;
+    int n_fullname;
+    int slant;
+    int weight;
+} ASS_FontProviderMetaData;
+
+ASS_FontSelector *
+ass_fontselect_init(ASS_Library *library,
+                    FT_Library ftlibrary, const char *family,
+                    const char *path);
+char *ass_font_select(ASS_FontSelector *priv, ASS_Library *lib,
+                      const char *family, unsigned bold, unsigned italic,
+                      int *index, uint32_t code);
+void ass_fontselect_free(ASS_FontSelector *priv);
+
+// Font provider functions
+ASS_FontProvider *ass_font_provider_new(ASS_FontSelector *selector,
+        ASS_FontProviderFuncs *funcs, void *data);
+int ass_font_provider_add_font(ASS_FontProvider *provider,
+        ASS_FontProviderMetaData *meta, const char *path, unsigned int index,
+        void *data);
+void ass_font_provider_free(ASS_FontProvider *provider);
+
+#endif                          /* LIBASS_FONTCONFIG_H */
index b99ed4866667c72441c96062b2bded8e9d2bff8f..3d94615e2f76ed40167b8413ac507512ee3287c7 100644 (file)
@@ -102,7 +102,6 @@ void update_font(ASS_Renderer *render_priv)
 {
     unsigned val;
     ASS_FontDesc desc;
-    desc.treat_family_as_pattern = render_priv->state.treat_family_as_pattern;
 
     if (render_priv->state.family[0] == '@') {
         desc.vertical = 1;
@@ -129,7 +128,7 @@ void update_font(ASS_Renderer *render_priv)
 
     render_priv->state.font =
         ass_font_new(render_priv->cache.font_cache, render_priv->library,
-                     render_priv->ftlibrary, render_priv->fontconfig_priv,
+                     render_priv->ftlibrary, render_priv->fontselect,
                      &desc);
     free(desc.family);
 
index 42758c890a1dc7175782264a4538619e494cc746..5122128eeaea4985c67b5bfc95104e57227d63e5 100644 (file)
@@ -147,8 +147,8 @@ void ass_renderer_done(ASS_Renderer *render_priv)
     }
     if (render_priv->ftlibrary)
         FT_Done_FreeType(render_priv->ftlibrary);
-    if (render_priv->fontconfig_priv)
-        fontconfig_done(render_priv->fontconfig_priv);
+    if (render_priv->fontselect)
+        ass_fontselect_free(render_priv->fontselect);
     ass_shaper_free(render_priv->shaper);
     free(render_priv->eimg);
     free(render_priv->text_info.glyphs);
@@ -1181,7 +1181,7 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
             ass_font_set_transform(info->font, info->scale_x,
                                    info->scale_y, NULL);
             FT_Glyph glyph =
-                ass_font_get_glyph(priv->fontconfig_priv, info->font,
+                ass_font_get_glyph(info->font,
                         info->symbol, info->face_index, info->glyph_index,
                         priv->settings.hinting, info->flags);
             if (glyph != NULL) {
@@ -2671,7 +2671,10 @@ ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
         && !render_priv->settings.frame_height)
         return 1;               // library not initialized
 
-    if (!render_priv->fontconfig_priv)
+    if (!render_priv->fontselect)
+        return 1;
+
+    if (render_priv->library != track->library)
         return 1;
 
     free_list_clear(render_priv);
index ceebea1f4d81e05a43d9ffbfe01a8cd06060bf22..b664c791257053e958ec434e32b4a50d833e7a23 100644 (file)
@@ -38,7 +38,7 @@ typedef struct ass_shaper ASS_Shaper;
 #include "ass_bitmap.h"
 #include "ass_cache.h"
 #include "ass_utils.h"
-#include "ass_fontconfig.h"
+#include "ass_fontselect.h"
 #include "ass_library.h"
 #include "ass_drawing.h"
 #include "ass_bitmap.h"
@@ -294,7 +294,7 @@ typedef struct {
 struct ass_renderer {
     ASS_Library *library;
     FT_Library ftlibrary;
-    FCInstance *fontconfig_priv;
+    ASS_FontSelector *fontselect;
     ASS_Settings settings;
     int render_id;
     ASS_Shaper *shaper;
index 2db653a6186ef3655c123f63b1a4d94f74ecab26..cfa8998d0ad06c0f3810f490384d1e3ba1f43f4d 100644 (file)
@@ -144,11 +144,10 @@ void ass_set_fonts(ASS_Renderer *priv, const char *default_font,
     priv->settings.default_family =
         default_family ? strdup(default_family) : 0;
 
-    if (priv->fontconfig_priv)
-        fontconfig_done(priv->fontconfig_priv);
-    priv->fontconfig_priv =
-        fontconfig_init(priv->library, priv->ftlibrary, default_family,
-                        default_font, fc, config, update);
+    if (priv->fontselect)
+        ass_fontselect_free(priv->fontselect);
+    priv->fontselect = ass_fontselect_init(priv->library, priv->ftlibrary,
+            default_family, default_font);
 }
 
 void ass_set_selective_style_override_enabled(ASS_Renderer *priv, int bits)
@@ -169,7 +168,8 @@ void ass_set_selective_style_override(ASS_Renderer *priv, ASS_Style *style)
 
 int ass_fonts_update(ASS_Renderer *render_priv)
 {
-    return fontconfig_update(render_priv->fontconfig_priv);
+    //return fontconfig_update(render_priv->fontselect);
+    return 1;
 }
 
 void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max,
index 41a48ef0b9461ddac2acbab51866368fe26c15fc..01642d1bfeb20041a547e3af5117a48338fb9bda 100644 (file)
@@ -733,7 +733,7 @@ void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
         if (info->symbol == 0xfffc)
             continue;
         // set size and get glyph index
-        ass_font_get_index(render_priv->fontconfig_priv, info->font,
+        ass_font_get_index(render_priv->fontselect, info->font,
                 info->symbol, &info->face_index, &info->glyph_index);
         // shape runs break on: xbord, ybord, xshad, yshad,
         // all four colors, all four alphas, be, blur, fn, fs,