font: initial support for fontconfig font fallback

When we've exhausted our own fallback list, try the font list provided
by fontconfig.

This means, the user's fallback fonts have priority, but in case all
of them fail, we hopefully get lucky with fontconfig...
This commit is contained in:
Daniel Eklöf 2019-08-10 20:34:22 +02:00
parent 75830ba016
commit 92319d1570
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 124 additions and 46 deletions

165
font.c
View file

@ -7,8 +7,6 @@
#include <assert.h>
#include <threads.h>
#include <fontconfig/fontconfig.h>
#define LOG_MODULE "font"
#define LOG_ENABLE_DBG 0
#include "log.h"
@ -47,62 +45,58 @@ font_populate_glyph_cache(struct font *font)
#endif
static bool
from_name(const char *base_name, const font_list_t *fallbacks, const char *attributes, struct font *font, bool is_fallback)
from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, const font_list_t *fallbacks,
const char *attributes, struct font *font, bool is_fallback)
{
memset(font, 0, sizeof(*font));
size_t attr_len = attributes == NULL ? 0 : strlen(attributes);
bool have_attrs = attr_len > 0;
char name[strlen(base_name) + (have_attrs ? 1 : 0) + attr_len + 1];
strcpy(name, base_name);
if (have_attrs){
strcat(name, ":");
strcat(name, attributes);
}
LOG_DBG("instantiating %s", name);
FcPattern *pattern = FcNameParse((const unsigned char *)name);
if (pattern == NULL) {
LOG_ERR("%s: failed to lookup font", name);
return false;
}
if (!FcConfigSubstitute(NULL, pattern, FcMatchPattern)) {
LOG_ERR("%s: failed to do config substitution", name);
FcPatternDestroy(pattern);
return false;
}
FcDefaultSubstitute(pattern);
FcResult result;
FcPattern *final_pattern = FcFontMatch(NULL, pattern, &result);
FcPatternDestroy(pattern);
if (final_pattern == NULL) {
LOG_ERR("%s: failed to match font", name);
return false;
}
FcChar8 *face_file = NULL;
if (FcPatternGetString(final_pattern, FC_FT_FACE, 0, &face_file) != FcResultMatch) {
if (FcPatternGetString(final_pattern, FC_FILE, 0, &face_file) != FcResultMatch) {
LOG_ERR("no font file name available");
FcPatternDestroy(final_pattern);
return false;
FcPattern *final_pattern = NULL;
int font_idx = -1;
for (int i = start_idx; i < fonts->nfont; i++) {
FcPattern *pat = FcFontRenderPrepare(NULL, pattern, fonts->fonts[i]);
assert(pat != NULL);
if (FcPatternGetString(pat, FC_FT_FACE, 0, &face_file) != FcResultMatch) {
if (FcPatternGetString(pat, FC_FILE, 0, &face_file) != FcResultMatch) {
FcPatternDestroy(pat);
continue;
}
}
final_pattern = pat;
font_idx = i;
break;
}
assert(font_idx != -1);
assert(final_pattern != NULL);
#if 0
if (is_fallback) {
FcFontSetDestroy(fonts);
FcPatternDestroy(pattern);
}
#endif
double dpi;
if (FcPatternGetDouble(final_pattern, FC_DPI, 0, &dpi) != FcResultMatch)
dpi = 96;
double size;
if (FcPatternGetDouble(final_pattern, FC_PIXEL_SIZE, 0, &size)) {
LOG_ERR("%s: failed to get size", name);
LOG_ERR("%s: failed to get size", face_file);
FcPatternDestroy(final_pattern);
#if 0
if (!is_fallback) {
FcFontSetDestroy(fonts);
FcPatternDestroy(pattern);
}
#endif
return false;
}
@ -211,6 +205,7 @@ from_name(const char *base_name, const font_list_t *fallbacks, const char *attri
font->render_flags = render_flags;
font->is_fallback = is_fallback;
font->pixel_size_fixup = scalable ? pixel_fixup : 1.;
font->fc_idx = font_idx;
if (fallbacks != NULL) {
tll_foreach(*fallbacks, it) {
@ -228,10 +223,63 @@ from_name(const char *base_name, const font_list_t *fallbacks, const char *attri
}
}
if (is_fallback)
return true;
return true;
}
static bool
from_name(const char *base_name, const font_list_t *fallbacks, const char *attributes, struct font *font, bool is_fallback)
{
//memset(font, 0, sizeof(*font));
size_t attr_len = attributes == NULL ? 0 : strlen(attributes);
bool have_attrs = attr_len > 0;
char name[strlen(base_name) + (have_attrs ? 1 : 0) + attr_len + 1];
strcpy(name, base_name);
if (have_attrs){
strcat(name, ":");
strcat(name, attributes);
}
LOG_DBG("instantiating %s", name);
FcPattern *pattern = FcNameParse((const unsigned char *)name);
if (pattern == NULL) {
LOG_ERR("%s: failed to lookup font", name);
return false;
}
if (!FcConfigSubstitute(NULL, pattern, FcMatchPattern)) {
LOG_ERR("%s: failed to do config substitution", name);
FcPatternDestroy(pattern);
return false;
}
FcDefaultSubstitute(pattern);
FcResult result;
FcFontSet *fonts = FcFontSort(NULL, pattern, FcTrue, NULL, &result);
if (result != FcResultMatch) {
LOG_ERR("%s: failed to match font", name);
FcPatternDestroy(pattern);
return false;
}
if (!from_font_set(pattern, fonts, 0, fallbacks, attributes, font, is_fallback)) {
FcFontSetDestroy(fonts);
FcPatternDestroy(pattern);
return false;
}
if (!is_fallback) {
font->fc_pattern = pattern;
font->fc_fonts = fonts;
font->cache = calloc(cache_size, sizeof(font->cache[0]));
} else {
FcFontSetDestroy(fonts);
FcPatternDestroy(pattern);
}
font->cache = calloc(cache_size, sizeof(font->cache[0]));
return true;
}
@ -240,7 +288,7 @@ font_from_name(font_list_t names, const char *attributes, struct font *font)
{
if (tll_length(names) == 0)
return false;
font_list_t fallbacks = tll_init();
bool skip_first = true;
tll_foreach(names, it) {
@ -281,8 +329,10 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph)
FT_UInt idx = FT_Get_Char_Index(font->face, wc);
if (idx == 0) {
/* No glyph in this font, try fallback fonts */
struct font fallback;
/* Try user configured fallback fonts */
tll_foreach(font->fallbacks, it) {
struct font fallback;
if (from_name(it->item, NULL, "", &fallback, true)) {
if (glyph_for_wchar(&fallback, wc, glyph)) {
LOG_DBG("%C: used fallback %s (fixup = %f)",
@ -298,6 +348,24 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph)
if (font->is_fallback)
return false;
/* Try fontconfig fallback fonts */
assert(font->fc_pattern != NULL);
assert(font->fc_fonts != NULL);
assert(font->fc_idx != -1);
for (int i = font->fc_idx + 1; i < font->fc_fonts->nfont; i++) {
if (from_font_set(font->fc_pattern, font->fc_fonts, i, NULL, "", &fallback, true)) {
if (glyph_for_wchar(&fallback, wc, glyph)) {
LOG_DBG("%C: used fontconfig fallback", wc);
font_destroy(&fallback);
return true;
}
font_destroy(&fallback);
}
}
LOG_WARN("%C: no glyph found (in neither the main font, "
"nor any fallback fonts)", wc);
}
@ -441,6 +509,11 @@ font_destroy(struct font *font)
mtx_destroy(&font->lock);
if (font->fc_pattern != NULL)
FcPatternDestroy(font->fc_pattern);
if (font->fc_fonts != NULL)
FcFontSetDestroy(font->fc_fonts);
if (font->cache == NULL)
return;

5
font.h
View file

@ -6,6 +6,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_LCD_FILTER_H
#include <fontconfig/fontconfig.h>
#include <cairo.h>
#include "tllist.h"
@ -28,6 +29,10 @@ struct glyph {
typedef tll(struct glyph) hash_entry_t;
struct font {
FcPattern *fc_pattern;
FcFontSet *fc_fonts;
int fc_idx;
FT_Face face;
int load_flags;
int render_flags;