From cd223f9dac6a71075f5bfbe59eae8de7cead59f9 Mon Sep 17 00:00:00 2001 From: rcombs Date: Mon, 23 Jan 2023 02:12:41 -0600 Subject: [PATCH] fontselect: overhaul font scoring and faux-ing; fixes #437 This emulates GDI's behavior, both around scoring and faux decisions. The fontconfig provider doesn't provide the full information we'd need to handle this 100% accurately, so it's best-guessed. --- libass/ass_font.c | 30 +++++++++++++++++++-- libass/ass_font.h | 1 + libass/ass_fontconfig.c | 6 ++++- libass/ass_fontselect.c | 59 +++++++++++++++++++++-------------------- libass/ass_fontselect.h | 2 +- 5 files changed, 65 insertions(+), 33 deletions(-) diff --git a/libass/ass_font.c b/libass/ass_font.c index 72ec88816..4faaa541e 100644 --- a/libass/ass_font.c +++ b/libass/ass_font.c @@ -548,6 +548,29 @@ int ass_face_get_weight(FT_Face face) } } +static FT_Long fsSelection_to_style_flags(uint16_t fsSelection) +{ + FT_Long ret = 0; + + if (fsSelection & 1) + ret |= FT_STYLE_FLAG_ITALIC; + if (fsSelection & (1 << 5)) + ret |= FT_STYLE_FLAG_BOLD; + + return ret; +} + +FT_Long ass_face_get_style_flags(FT_Face face) +{ + // If we have an OS/2 table, compute this ourselves, since FreeType + // will mix in some flags that GDI ignores. + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2); + if (os2) + return fsSelection_to_style_flags(os2->fsSelection); + + return face->style_flags; +} + /** * \brief Get maximal font ascender and descender. **/ @@ -693,9 +716,12 @@ bool ass_font_get_glyph(ASS_Font *font, int face_index, int index, index); return false; } - if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && (font->desc.italic > 55)) + + FT_Long style_flags = ass_face_get_style_flags(face); + if (!(style_flags & FT_STYLE_FLAG_ITALIC) && (font->desc.italic > 55)) ass_glyph_italicize(face->glyph); - if (font->desc.bold > ass_face_get_weight(face) + 150) + if (!(style_flags & FT_STYLE_FLAG_BOLD) && + font->desc.bold > ass_face_get_weight(face) + 150) ass_glyph_embolden(face->glyph); return true; } diff --git a/libass/ass_font.h b/libass/ass_font.h index df9fb3050..b92f90d0f 100644 --- a/libass/ass_font.h +++ b/libass/ass_font.h @@ -52,6 +52,7 @@ void ass_charmap_magic(ASS_Library *library, FT_Face face); ASS_Font *ass_font_new(ASS_Renderer *render_priv, ASS_FontDesc *desc); void ass_face_set_size(FT_Face face, double size); int ass_face_get_weight(FT_Face face); +FT_Long ass_face_get_style_flags(FT_Face face); void ass_font_get_asc_desc(ASS_Font *font, int face_index, int *asc, int *desc); int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font, diff --git a/libass/ass_fontconfig.c b/libass/ass_fontconfig.c index 63c9cb14c..0b6fac057 100644 --- a/libass/ass_fontconfig.c +++ b/libass/ass_fontconfig.c @@ -98,6 +98,7 @@ static void scan_fonts(FcConfig *config, ASS_FontProvider *provider) FcBool outline; int index; double weight; + int slant; char *path; char *fullnames[MAX_NAME]; char *families[MAX_NAME]; @@ -108,7 +109,7 @@ static void scan_fonts(FcConfig *config, ASS_FontProvider *provider) continue; // simple types - result = FcPatternGetInteger(pat, FC_SLANT, 0, &meta.slant); + result = FcPatternGetInteger(pat, FC_SLANT, 0, &slant); result |= FcPatternGetDouble(pat, FC_WEIGHT, 0, &weight); result |= FcPatternGetInteger(pat, FC_INDEX, 0, &index); if (result != FcResultMatch) @@ -158,6 +159,9 @@ static void scan_fonts(FcConfig *config, ASS_FontProvider *provider) meta.weight = 1000; #endif + // Take a guess at the italic flag + meta.style_flags = (slant >= FC_SLANT_ITALIC) ? FT_STYLE_FLAG_ITALIC : 0; + // path result = FcPatternGetString(pat, FC_FILE, 0, (FcChar8 **)&path); if (result != FcResultMatch) diff --git a/libass/ass_fontselect.c b/libass/ass_fontselect.c index a32004784..88c8e7f5a 100644 --- a/libass/ass_fontselect.c +++ b/libass/ass_fontselect.c @@ -34,6 +34,7 @@ #include FT_FREETYPE_H #include FT_SFNT_NAMES_H #include FT_TRUETYPE_IDS_H +#include FT_TRUETYPE_TABLES_H #include FT_TYPE1_TABLES_H #include "ass_utils.h" @@ -60,8 +61,8 @@ struct font_info { int n_family; int n_fullname; - int slant; - int weight; // TrueType scale, 100-900 + FT_Long style_flags; + int weight; // TrueType scale, 100-900 // how to access this face char *path; // absolute path @@ -256,7 +257,6 @@ get_font_info(FT_Library lib, FT_Face face, const char *fallback_family_name, int num_fullname = 0; int num_family = 0; int num_names = FT_Get_Sfnt_Name_Count(face); - int slant, weight; char *fullnames[MAX_FULLNAME]; char *families[MAX_FULLNAME]; PS_FontInfoRec postscript_info; @@ -309,13 +309,9 @@ get_font_info(FT_Library lib, FT_Face face, const char *fallback_family_name, if (num_family == 0) goto error; - // calculate sensible slant and weight from style attributes - slant = 110 * !!(face->style_flags & FT_STYLE_FLAG_ITALIC); - weight = ass_face_get_weight(face); - - // fill our struct - info->slant = slant; - info->weight = weight; + // calculate sensible weight + info->weight = ass_face_get_weight(face); + info->style_flags = ass_face_get_style_flags(face); info->postscript_name = (char *)FT_Get_Postscript_Name(face); info->is_postscript = !FT_Get_PS_Font_Info(face, &postscript_info); @@ -389,7 +385,6 @@ ass_font_provider_add_font(ASS_FontProvider *provider, int index, void *data) { int i; - int weight, slant; ASS_FontSelector *selector = provider->parent; ASS_FontInfo *info = NULL; ASS_FontProviderMetaData implicit_meta = {0}; @@ -445,21 +440,12 @@ ass_font_provider_add_font(ASS_FontProvider *provider, for (j = 0; j < meta->n_fullname; j++) printf("'%s' ", meta->fullnames[j]); printf("\n"); - printf(" slant: %d\n", meta->slant); + printf(" style_flags: %lx\n", meta->style_flags); printf(" weight: %d\n", meta->weight); printf(" path: %s\n", path); printf(" index: %d\n", index); #endif - weight = meta->weight; - slant = meta->slant; - - // check slant/weight for validity, use defaults if they're invalid - if (weight < 100 || weight > 900) - weight = 400; - if (slant < 0 || slant > 110) - slant = 0; - // check size if (selector->n_font >= selector->alloc_font) { selector->alloc_font = FFMAX(1, 2 * selector->alloc_font); @@ -474,8 +460,8 @@ ass_font_provider_add_font(ASS_FontProvider *provider, // set uid info->uid = selector->uid++; - info->slant = slant; - info->weight = weight; + info->style_flags = meta->style_flags; + info->weight = meta->weight; info->n_fullname = meta->n_fullname; info->n_family = meta->n_family; info->is_postscript = meta->is_postscript; @@ -668,11 +654,26 @@ static bool matches_full_or_postscript_name(ASS_FontInfo *f, */ static unsigned font_attributes_similarity(ASS_FontInfo *a, ASS_FontInfo *req) { - unsigned similarity = 0; - similarity += ABS(a->weight - req->weight); - similarity += ABS(a->slant - req->slant); + unsigned score = 0; + + // Assign score for italics mismatch + if ((req->style_flags & FT_STYLE_FLAG_ITALIC) && + !(a->style_flags & FT_STYLE_FLAG_ITALIC)) + score += 1; + else if (!(req->style_flags & FT_STYLE_FLAG_ITALIC) && + (a->style_flags & FT_STYLE_FLAG_ITALIC)) + score += 4; + + int a_weight = a->weight; + + // Offset effective weight for faux-bold (only if font isn't flagged as bold) + if ((req->weight > a->weight + 150) && !(a->style_flags & FT_STYLE_FLAG_BOLD)) + a_weight += 120; + + // Assign score for weight mismatch + score += (73 * ABS(a_weight - req->weight)) / 256; - return similarity; + return score; } #if 0 @@ -724,8 +725,8 @@ find_font(ASS_FontSelector *priv, return NULL; // fill font request - req.slant = italic; - req.weight = bold; + req.style_flags = (italic ? FT_STYLE_FLAG_ITALIC : 0); + req.weight = bold; // Match font family name against font list unsigned score_min = UINT_MAX; diff --git a/libass/ass_fontselect.h b/libass/ass_fontselect.h index bc9c46731..6c6b597fb 100644 --- a/libass/ass_fontselect.h +++ b/libass/ass_fontselect.h @@ -205,7 +205,7 @@ struct ass_font_provider_meta_data { int n_family; // Number of localized family names int n_fullname; // Number of localized full names - int slant; // Font slant value from FONT_SLANT_* + FT_Long style_flags; // Computed from OS/2 table, or equivalent int weight; // Font weight in TrueType scale, 100-900 // See FONT_WEIGHT_*