libass
libass copied to clipboard
Discuss: Plans for Android AFont font provider
The Android NDK functions applicable to font selection are documented here: https://developer.android.com/ndk/reference/group/font
Using this API conditionally depending on the API's existence should be fairly easy using something like this:
#define DECLARE_FUNCPTR(name) __typeof__(name) *p##name;
typedef struct afont_funcs {
DECLARE_FUNCPTR(AFontMatcher_create)
DECLARE_FUNCPTR(AFontMatcher_destroy)
DECLARE_FUNCPTR(AFontMatcher_match)
DECLARE_FUNCPTR(AFontMatcher_setLocales)
DECLARE_FUNCPTR(AFontMatcher_setStyle)
DECLARE_FUNCPTR(AFont_close)
DECLARE_FUNCPTR(AFont_getFontFilePath)
DECLARE_FUNCPTR(AFont_getCollectionIndex)
} AFontFuncs;
AFontFuncs *get_funcs(void)
{
AFontFuncs *funcs = calloc(sizeof(AFontFuncs), 1);
#define FILL_FUNCPTR(name) if (!(funcs->p##name = dlsym(RTLD_DEFAULT, #name))) goto fail;
FILL_FUNCPTR(AFontMatcher_create)
FILL_FUNCPTR(AFontMatcher_destroy)
FILL_FUNCPTR(AFontMatcher_match)
FILL_FUNCPTR(AFontMatcher_setLocales)
FILL_FUNCPTR(AFontMatcher_setStyle)
FILL_FUNCPTR(AFont_close)
FILL_FUNCPTR(AFont_getFontFilePath)
FILL_FUNCPTR(AFont_getCollectionIndex)
return funcs;
fail:
free(funcs);
return NULL;
}
The hard parts come from the API's limitations:
- There's no way to get a list of fonts that matched a name;
AFontMatcher_match
always returns a single font. - There's no way to get a font matching a name without passing in text;
AFontMatcher_match
requires at least 1 character. - There's no way to get a name for the returned font;
AFont
only provides a path and index.
So, I think there are a few major changes that'll be needed here:
- First, we should attempt to find a match using memory fonts only, then search system fonts. These should really be separate lists.
- When searching system fonts, we should give the font selection mechanism an opportunity to perform a complete match and return a single font (path/stream + name/ID) directly, rather than insert elements into our internal search list.
- (What text should we use if
code
== 0? Maybe" "
?)
- (What text should we use if
This means that Provider fonts may never enter ASS_FontProviderMetaData
, or would only enter a secondary internal list that isn't used for our internal first-pass matching.
This mechanism is entirely reasonable for fallback/unstyled cases, but isn't suitable for authoring. In authoring mode (…a thing we should have), we should fall back on fontconfig, or the slow-but-cross-platform "load every font on the system" method. This probably isn't particularly relevant to Android, but we may want to transition other platforms to this new mechanism for performance and locale-handling reasons.
Any thoughts?
How does this relate to #312?
See also my own plans/hopes for reworked font providers in https://github.com/libass/libass/pull/511#issuecomment-873572826.
First, we should attempt to find a match using memory fonts only, then search system fonts.
We already do this, and we are too eager to avoid system fonts; see #509. In the sense that this is incompatible with VSFilter: it always merges attached fonts with system fonts. Of course, one could argue that it’s undesirable for script portability etc., but that’s when the fabled authoring mode comes up, and we’ve generally stuck to striving for VSFilter compatibility in this respect as far as each font provider backend allows.
These should really be separate lists.
Agree.
What text should we use if
code
== 0? Maybe" "
?
Yeah, I’m thinking of U+0020 SPACE, too. It’s a very ubiquitous glyph, and even HarfBuzz uses it for rendering invisible characters by default.
When searching system fonts, we should give the font selection mechanism an opportunity to perform a complete match and return a single font (path/stream + name/ID) directly, rather than […] enter
ASS_FontProviderMetaData
(Correct me if I got that abridgement wrong.)
We could load the file at the given path and read & return all fonts from it, with ASS_FontProviderMetaData
and all. This would trivially work for single-font files and could be extra beneficial for single-family multiple-font collection files, where we could then (in our usual manner) refine the result by attributes other than the name.
This replaces #312. It has the downside of only working on recent-ish Android, but consumers that want to support older versions can point a fontconfig .conf file at /system/fonts
and get better performance anyway. I don't like #312's approach, both because it relies on internal data that's explicitly stated not to be meant for application usage, and because it individually opens and loads every single installed font, which is problematic performance-wise.
The concept I'm suggesting here seems to be more or less the same as what you described in #511, so I think we're more or less on the same page here.