summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-08-28 13:54:33 +0200
committerwm4 <wm4@nowhere>2015-08-28 14:06:54 +0200
commitdb1c6fd2a34c331825bfb6cbdef313fa9e64528e (patch)
tree890cd088cd5a8ace12c567596f73c6926dbd5536
parent2d8c4d74d40bc6ae8c79cda36641b9f450ef92ef (diff)
downloadlibass-db1c6fd2a34c331825bfb6cbdef313fa9e64528e.tar.bz2
libass-db1c6fd2a34c331825bfb6cbdef313fa9e64528e.tar.xz
fontselect: correctly match list of font substitutions
This code prioritized fonts by scanning order, but the scanning order is essentially arbitrary. This resulted in suboptimal and indeterministic font selection by family name. Prefer the order as returned by the substitution callback. This requires some restructuring. The core algorithm is still exactly the same though; only the order of comparisons changes. If the font backend has no subtitution callback, nothing should change.
-rw-r--r--libass/ass_fontselect.c138
1 files changed, 72 insertions, 66 deletions
diff --git a/libass/ass_fontselect.c b/libass/ass_fontselect.c
index 2653ed7..a603fbc 100644
--- a/libass/ass_fontselect.c
+++ b/libass/ass_fontselect.c
@@ -379,54 +379,46 @@ void ass_font_provider_free(ASS_FontProvider *provider)
free(provider);
}
+/**
+ * \brief Return whether the given font is in the given family.
+ */
+static bool matches_family_name(ASS_FontInfo *f, const char *family)
+{
+ for (int i = 0; i < f->n_family; i++) {
+ if (strcasecmp(f->families[i], family) == 0)
+ return true;
+ }
+ return false;
+}
+/**
+ * \brief Return whether the given font has the given fullname.
+ */
+static bool matches_fullname(ASS_FontInfo *f, const char *fullname)
+{
+ for (int i = 0; i < f->n_fullname; i++) {
+ if (strcasecmp(f->fullnames[i], fullname) == 0)
+ return true;
+ }
+ return false;
+}
/**
- * \brief Compare a font (a) against a font request (b). Records
+ * \brief Compare attributes of font (a) against a font request (req). Returns
* a matching score - the lower the better.
+ * Ignores font names/families!
* \param a font
* \param b font request
* \return matching score
*/
-static unsigned font_info_similarity(ASS_FontInfo *a, ASS_FontInfo *req)
+static unsigned font_attributes_similarity(ASS_FontInfo *a, ASS_FontInfo *req)
{
- int i, j;
- int family_match = 0;
-
- // Compare family name first; sometimes family name equals fullname,
- // but we want to be able to match against the different variants
- // in case a family name match occurs.
- for (j = 0; j < req->n_fullname; j++) {
- for (i = 0; i < a->n_family; i++) {
- if (strcasecmp(a->families[i], req->fullnames[j]) == 0) {
- family_match = 1;
- break;
- }
- }
- }
-
- // If there's a family match, compare font attributes
- // to determine best match in that particular family
- if (family_match) {
- unsigned similarity = 0;
- similarity += ABS(a->weight - req->weight);
- similarity += ABS(a->slant - req->slant);
- similarity += ABS(a->width - req->width);
-
- return similarity;
- }
-
- // If we don't have any match, compare fullnames against request
- // if there is a match now, assign lowest score possible. This means
- // the font should be chosen instantly, without further search.
- for (j = 0; j < req->n_fullname; j++) {
- for (i = 0; i < a->n_fullname; i++) {
- if (strcasecmp(a->fullnames[i], req->fullnames[j]) == 0)
- return 0;
- }
- }
+ unsigned similarity = 0;
+ similarity += ABS(a->weight - req->weight);
+ similarity += ABS(a->slant - req->slant);
+ similarity += ABS(a->width - req->width);
- return UINT_MAX;
+ return similarity;
}
#if 0
@@ -489,12 +481,10 @@ static char *select_font(ASS_FontSelector *priv, ASS_Library *library,
// get a list of substitutes if applicable, and use it for matching
if (default_provider && default_provider->funcs.subst_font) {
default_provider->funcs.subst_font(default_provider->priv, family_trim, &meta);
- req.n_fullname = meta.n_fullname;
- req.fullnames = meta.fullnames;
}
- if (!req.n_fullname) {
- req.n_fullname = 1;
- req.fullnames = &family_trim;
+ if (!meta.n_fullname) {
+ meta.n_fullname = 1;
+ meta.fullnames = &family_trim;
}
// fill font request
@@ -504,31 +494,47 @@ static char *select_font(ASS_FontSelector *priv, ASS_Library *library,
// Match font family name against font list
unsigned score_min = UINT_MAX;
- for (int i = 0; i < priv->n_font; i++) {
- unsigned score = font_info_similarity(&priv->font_infos[i], &req);
-
- // Consider updating idx if score is better than current minimum
- if (score < score_min) {
- // Check if the font has the requested glyph.
- // We are doing this here, for every font face, because
- // coverage might differ between the variants of a font
- // family. In practice, it is common that the regular
- // style has the best coverage while bold/italic/etc
- // variants cover less (e.g. FreeSans family).
- // We want to be able to match even if the closest variant
- // does not have the requested glyph, but another member
- // of the family has the glyph.
- if (!check_glyph(&priv->font_infos[i], code))
- continue;
+ for (int i = 0; i < meta.n_fullname; i++) {
+ const char *fullname = meta.fullnames[i];
+
+ for (int x = 0; x < priv->n_font; x++) {
+ ASS_FontInfo *font = &priv->font_infos[x];
+ unsigned score = UINT_MAX;
+
+ if (matches_family_name(font, fullname)) {
+ // If there's a family match, compare font attributes
+ // to determine best match in that particular family
+ score = font_attributes_similarity(font, &req);
+ } else if (matches_fullname(font, fullname)) {
+ // If we don't have any match, compare fullnames against request
+ // if there is a match now, assign lowest score possible. This means
+ // the font should be chosen instantly, without further search.
+ score = 0;
+ }
- score_min = score;
- idx = i;
- }
+ // Consider updating idx if score is better than current minimum
+ if (score < score_min) {
+ // Check if the font has the requested glyph.
+ // We are doing this here, for every font face, because
+ // coverage might differ between the variants of a font
+ // family. In practice, it is common that the regular
+ // style has the best coverage while bold/italic/etc
+ // variants cover less (e.g. FreeSans family).
+ // We want to be able to match even if the closest variant
+ // does not have the requested glyph, but another member
+ // of the family has the glyph.
+ if (!check_glyph(&priv->font_infos[i], code))
+ continue;
+
+ score_min = score;
+ idx = x;
+ }
- // Lowest possible score instantly matches; this is typical
- // for fullname matches, but can also occur with family matches.
- if (score == 0)
- break;
+ // Lowest possible score instantly matches; this is typical
+ // for fullname matches, but can also occur with family matches.
+ if (score == 0)
+ break;
+ }
}
// free font name