From db85ef4736108c12bf3e12939447840815c246ec Mon Sep 17 00:00:00 2001 From: Oleg Oshmyan Date: Sat, 27 Mar 2021 04:12:58 +0200 Subject: fontselect: coretext: get fallback font family name via FreeType Since commit be0d1613f79a95073d18d96a60e1394abf9316a2, get_fallback can return a name from Core Text that is different from all names that match_fonts registers for the same font. This causes font fallback to fail as find_fonts fails to find any font having the name received from get_fallback. (Moreover, libass will keep retrying fallback for other glyphs, and the coretext match_fonts will keep readding the same fonts over and over again.) Commit 152d0484e98f118d01987138695cf40579c9e297 attempted to fix font fallback by getting a full name from Core Text instead of a family name when it was noticed that Core Text's family name can come from TT_NAME_ID_TYPOGRAPHIC_FAMILY in addition to TT_NAME_ID_FONT_FAMILY, which we fetch in get_font_info, but the problem goes deeper: Core Text can return Macintosh-platform names that don't match Microsoft-platform names, or the font might have no Microsoft-platform names at all. Furthermore, returning a full name breaks style matching: get_fallback does not know about weight, slant etc. and is expected to return a whole family of fonts that will be lazy-loaded in its entirety (if applicable) and searched through for the best stylistic match by find_fonts. To fix fallback while preserving maximum name portability in the spirit of be0d1613f79a95073d18d96a60e1394abf9316a2, use the same API in get_fallback as is used in match_fonts to look up a family name. Note: this could be more efficient if ass_get_font_info could be told to return just an arbitrary family name. This fixes https://github.com/libass/libass/issues/457. --- libass/ass_coretext.c | 32 +++++++++++++++++++++++++------- libass/ass_directwrite.c | 3 ++- libass/ass_fontconfig.c | 3 ++- libass/ass_fontselect.c | 2 +- libass/ass_fontselect.h | 2 ++ 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/libass/ass_coretext.c b/libass/ass_coretext.c index 49a96ff..f1063ba 100644 --- a/libass/ass_coretext.c +++ b/libass/ass_coretext.c @@ -246,8 +246,11 @@ static void match_fonts(void *priv, ASS_Library *lib, ASS_FontProvider *provider SAFE_CFRelease(cfname); } -static char *get_fallback(void *priv, const char *family, uint32_t codepoint) +static char *get_fallback(void *priv, ASS_Library *lib, + const char *family, uint32_t codepoint) { + FT_Library ftlib = priv; + CFStringRef name = CFStringCreateWithBytes( 0, (UInt8 *)family, strlen(family), kCFStringEncodingUTF8, false); CTFontRef font = CTFontCreateWithName(name, 0, NULL); @@ -256,17 +259,32 @@ static char *get_fallback(void *priv, const char *family, uint32_t codepoint) 0, (UInt8*)&codepointle, sizeof(codepointle), kCFStringEncodingUTF32LE, false); CTFontRef fb = CTFontCreateForString(font, r, CFRangeMake(0, 1)); - CFNumberRef cfformat = CTFontCopyAttribute(fb, kCTFontFormatAttribute); - CFStringRef cfname = is_postscript_font_format(cfformat) ? - CTFontCopyPostScriptName(fb) : CTFontCopyFullName(fb); - char *res_name = cfstr2buf(cfname); + CTFontDescriptorRef fontd = CTFontCopyFontDescriptor(fb); + + char *res_name = NULL; + char *path = NULL; + ASS_FontProviderMetaData meta = {0}; + if (get_font_info_ct(lib, ftlib, fontd, &path, &meta)) + res_name = meta.families[0]; + + for (int i = 1 /* skip res_name */; i < meta.n_family; i++) + free(meta.families[i]); + + for (int i = 0; i < meta.n_fullname; i++) + free(meta.fullnames[i]); + + free(meta.families); + free(meta.fullnames); + + free(meta.postscript_name); + + free(path); SAFE_CFRelease(name); SAFE_CFRelease(font); SAFE_CFRelease(r); SAFE_CFRelease(fb); - SAFE_CFRelease(cfformat); - SAFE_CFRelease(cfname); + SAFE_CFRelease(fontd); return res_name; } diff --git a/libass/ass_directwrite.c b/libass/ass_directwrite.c index 49ab2d9..0ad9118 100644 --- a/libass/ass_directwrite.c +++ b/libass/ass_directwrite.c @@ -424,7 +424,8 @@ static int encode_utf16(wchar_t *chars, uint32_t codepoint) } } -static char *get_fallback(void *priv, const char *base, uint32_t codepoint) +static char *get_fallback(void *priv, ASS_Library *lib, + const char *base, uint32_t codepoint) { HRESULT hr; ProviderPrivate *provider_priv = (ProviderPrivate *)priv; diff --git a/libass/ass_fontconfig.c b/libass/ass_fontconfig.c index 1e33106..a4ffb42 100644 --- a/libass/ass_fontconfig.c +++ b/libass/ass_fontconfig.c @@ -220,7 +220,8 @@ static void cache_fallbacks(ProviderPrivate *fc) FcPatternDestroy(pat); } -static char *get_fallback(void *priv, const char *family, uint32_t codepoint) +static char *get_fallback(void *priv, ASS_Library *lib, + const char *family, uint32_t codepoint) { ProviderPrivate *fc = (ProviderPrivate *)priv; FcResult result; diff --git a/libass/ass_fontselect.c b/libass/ass_fontselect.c index 6b62511..3013f4a 100644 --- a/libass/ass_fontselect.c +++ b/libass/ass_fontselect.c @@ -717,7 +717,7 @@ char *ass_font_select(ASS_FontSelector *priv, ASS_Library *library, if (!search_family || !*search_family) search_family = "Arial"; char *fallback_family = default_provider->funcs.get_fallback( - default_provider->priv, search_family, code); + default_provider->priv, library, search_family, code); if (fallback_family) { res = select_font(priv, library, fallback_family, bold, italic, diff --git a/libass/ass_fontselect.h b/libass/ass_fontselect.h index d029b0b..942ea00 100644 --- a/libass/ass_fontselect.h +++ b/libass/ass_fontselect.h @@ -144,12 +144,14 @@ typedef void (*SubstituteFontFunc)(void *priv, const char *name, * fallbacks. * * \param priv font provider private data + * \param lib ASS_Library instance * \param family original font family name (try matching a similar font) (never NULL) * \param codepoint Unicode codepoint (UTF-32) * \return output font family, allocated with malloc(), must be freed * by caller. */ typedef char *(*GetFallbackFunc)(void *priv, + ASS_Library *lib, const char *family, uint32_t codepoint); -- cgit v1.2.3