From f6977417e0a022d29c472f37784922e4806c67c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 9 Jan 2019 18:30:35 +0100 Subject: [PATCH] font: use font-config to load font This allows us to a) move away from cairo's "toy" API, and b) let the user specify font options in a single font "name" string: Serif:size=10:weight=bold:slant=italic This also allows us to simplify the font code significantly (except for the fontconfig parts...); the font no longer sets itself in a cairo surface - font users do that; the font simply returns a cairo_scaled_font_t. Furthermore, font_clone() has now been simplified to basically just refcount the scaled font. I.e. there's no need to run the full constructor and lookup and instantiate the cairo scaled font again. --- CMakeLists.txt | 6 +- config-verify.c | 7 +- config.c | 17 ++--- font.c | 165 +++++++++++++++++++++++++++++---------------- font.h | 11 +-- particles/string.c | 70 ++++++++++++------- particles/string.h | 2 +- 7 files changed, 167 insertions(+), 111 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 533a34e..a0a0619 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,8 @@ find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(XCB REQUIRED xcb xcb-randr xcb-render xcb-cursor) # Core -pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb) # Core +pkg_check_modules(FONTCONFIG REQUIRED fontconfig) # Core +pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb cairo-ft) # Core pkg_check_modules(YAML REQUIRED yaml-0.1) # Core (configuration) pkg_check_modules(XCB_XKB REQUIRED xcb-xkb) # Module/xkb @@ -67,6 +68,7 @@ target_compile_definitions(f00bar PRIVATE _GNU_SOURCE) target_compile_options(f00bar PRIVATE ${XCB_CFLAGS_OTHER} + ${FONTCONFIG_CFLAGS_OTHER} ${CAIRO_CFLAGS_OTHER} ${YAML_CFLAGS_OTHER} ${XCB_XKB_CFLAGS_OTHER} @@ -78,6 +80,7 @@ target_compile_options(f00bar PRIVATE target_include_directories(f00bar PRIVATE ${XCB_INCLUDE_DIRS} + ${FONTCONFIG_INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS} ${YAML_INCLUDE_DIRS} ${XCB_XKB_INCLUDE_DIRS} @@ -90,6 +93,7 @@ target_include_directories(f00bar PRIVATE target_link_libraries(f00bar ${CMAKE_THREAD_LIBS_INIT} ${XCB_LIBRARIES} + ${FONTCONFIG_LIBRARIES} ${CAIRO_LIBRARIES} ${YAML_LIBRARIES} ${XCB_XKB_LIBRARIES} diff --git a/config-verify.c b/config-verify.c index 15e0a3a..32365a2 100644 --- a/config-verify.c +++ b/config-verify.c @@ -121,12 +121,7 @@ verify_font(keychain_t *chain, const struct yml_node *node) return false; } - if (strcmp(sub_key, "size") == 0 || - strcmp(sub_key, "y_offset") == 0) - { - if (!verify_int(chain_push(chain, sub_key), it.value)) - return false; - } else if (strcmp(sub_key, "family") == 0) { + if (strcmp(sub_key, "family") == 0) { if (!verify_string(chain_push(chain, sub_key), it.value)) return false; } else { diff --git a/config.c b/config.c index 2002c6f..6f490dd 100644 --- a/config.c +++ b/config.c @@ -86,17 +86,7 @@ static struct font * font_from_config(const struct yml_node *node) { const struct yml_node *family = yml_get_value(node, "family"); - const struct yml_node *size = yml_get_value(node, "size"); - const struct yml_node *italic = yml_get_value(node, "italic"); - const struct yml_node *bold = yml_get_value(node, "bold"); - const struct yml_node *y_offset = yml_get_value(node, "y_offset"); - - return font_new( - family != NULL ? yml_value_as_string(family) : "monospace", - size != NULL ? yml_value_as_int(size) : 12, - italic != NULL ? yml_value_as_bool(italic) : false, - bold != NULL ? yml_value_as_bool(bold) : false, - y_offset != NULL ? yml_value_as_int(y_offset) : 0); + return font_new(family != NULL ? yml_value_as_string(family) : "monospace"); } static struct deco * @@ -191,10 +181,12 @@ particle_string_from_config(const struct yml_node *node, assert(yml_is_dict(node)); const struct yml_node *text = yml_get_value(node, "text"); + const struct yml_node *max = yml_get_value(node, "max"); const struct yml_node *font = yml_get_value(node, "font"); const struct yml_node *foreground = yml_get_value(node, "foreground"); assert(text != NULL && yml_is_scalar(text)); + assert(max == NULL || yml_value_is_int(max)); struct rgba fg_color = foreground != NULL ? color_from_hexstr(yml_value_as_string(foreground)) : @@ -202,6 +194,7 @@ particle_string_from_config(const struct yml_node *node, return particle_string_new( yml_value_as_string(text), + max != NULL ? yml_value_as_int(max) : 0, font != NULL ? font_from_config(font) : font_clone(parent_font), fg_color, left_margin, right_margin, on_click_template); } @@ -597,7 +590,7 @@ conf_to_bar(const struct yml_node *bar) struct bar_config conf = {0}; /* Create a default font */ - struct font *font = font_new("sans", 12, false, false, 0); + struct font *font = font_new("sans"); const struct yml_node *height = yml_get_value(bar, "height"); const struct yml_node *location = yml_get_value(bar, "location"); diff --git a/font.c b/font.c index 50f8aca..d0a8114 100644 --- a/font.c +++ b/font.c @@ -2,32 +2,107 @@ #include #include #include +#include + +#include +#include + +#define LOG_MODULE "font" +#define LOG_ENABLE_DBG 0 +#include "log.h" struct font { - char *face; - int size; - bool italic; - bool bold; - - int y_offset; - cairo_font_options_t *cairo_font_options; + char *name; + cairo_scaled_font_t *scaled_font; }; -struct font * -font_new(const char *face, int size, bool italic, bool bold, int y_offset) +static void __attribute__((constructor)) +init(void) { + FcInit(); + +#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG + int raw_version = FcGetVersion(); + + /* See FC_VERSION in */ + const int major = raw_version / 10000; raw_version %= 10000; + const int minor = raw_version / 100; raw_version %= 100; + const int patch = raw_version; +#endif + + LOG_DBG("fontconfig: %d.%d.%d", major, minor, patch); +} + +static void __attribute__((destructor)) +fini(void) +{ + FcFini(); +} + +struct font * +font_new(const char *name) +{ + FcPattern *pattern = FcNameParse((const unsigned char *)name); + if (pattern == NULL) { + LOG_ERR("%s: failed to lookup font", name); + return NULL; + } + + if (!FcConfigSubstitute(NULL, pattern, FcMatchPattern)) { + LOG_ERR("%s: failed to do config substitution", name); + FcPatternDestroy(pattern); + return NULL; + } + + 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 NULL; + } + + double font_size; + if (FcPatternGetDouble(final_pattern, FC_PIXEL_SIZE, 0, &font_size)) { + LOG_ERR("%s: failed to get size", name); + FcPatternDestroy(final_pattern); + return NULL; + } + + cairo_font_face_t *face = cairo_ft_font_face_create_for_pattern( + final_pattern); + + FcPatternDestroy(final_pattern); + + if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS) { + LOG_ERR("%s: failed to create cairo font face", name); + cairo_font_face_destroy(face); + return NULL; + } + + cairo_matrix_t matrix, ctm; + cairo_matrix_init_identity(&ctm); + cairo_matrix_init_scale(&matrix, font_size, font_size); + + cairo_font_options_t *options = cairo_font_options_create(); + cairo_scaled_font_t *scaled_font = cairo_scaled_font_create( + face, &matrix, &ctm, options); + + cairo_font_options_destroy(options); + cairo_font_face_destroy(face); + + if (cairo_scaled_font_status(scaled_font) != CAIRO_STATUS_SUCCESS) { + LOG_ERR("%s: failed to create scaled font", name); + cairo_scaled_font_destroy(scaled_font); + return NULL; + } + struct font *font = malloc(sizeof(*font)); - - font->face = strdup(face); - font->size = size; - font->italic = italic; - font->bold = bold; - font->y_offset = y_offset; - - font->cairo_font_options = cairo_font_options_create(); - cairo_font_options_set_antialias( - font->cairo_font_options, CAIRO_ANTIALIAS_DEFAULT); - //antialias ? CAIRO_ANTIALIAS_SUBPIXEL : CAIRO_ANTIALIAS_NONE); + font->name = strdup(name); + font->scaled_font = scaled_font; return font; } @@ -35,58 +110,30 @@ font_new(const char *face, int size, bool italic, bool bold, int y_offset) struct font * font_clone(const struct font *font) { - return font_new(font->face, font->size, font->italic, font->bold, font->y_offset); + struct font *clone = malloc(sizeof(*font)); + clone->name = strdup(font->name); + clone->scaled_font = font->scaled_font; + + cairo_scaled_font_reference(clone->scaled_font); + return clone; } void font_destroy(struct font *font) { - cairo_font_options_destroy(font->cairo_font_options); - free(font->face); + cairo_scaled_font_destroy(font->scaled_font); + free(font->name); free(font); } const char * font_face(const struct font *font) { - return font->face; -} - -int -font_size(const struct font *font) -{ - return font->size; -} - -bool -font_is_italic(const struct font *font) -{ - return font->italic; -} - -bool -font_is_bold(const struct font *font) -{ - return font->bold; -} - -int -font_y_offset(const struct font *font) -{ - return font->y_offset; + return font->name; } cairo_scaled_font_t * -font_use_in_cairo(const struct font *font, cairo_t *cr) +font_scaled_font(const struct font *font) { - cairo_font_slant_t slant = font->italic - ? CAIRO_FONT_SLANT_ITALIC : CAIRO_FONT_SLANT_NORMAL; - cairo_font_weight_t weight = font->bold - ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL; - - cairo_select_font_face(cr, font->face, slant, weight); - cairo_set_font_size(cr, font->size); - cairo_set_font_options(cr, font->cairo_font_options); - - return cairo_get_scaled_font(cr); + return font->scaled_font; } diff --git a/font.h b/font.h index b8f10c6..45c8987 100644 --- a/font.h +++ b/font.h @@ -7,16 +7,9 @@ struct font; -struct font *font_new( - const char *face, int size, bool italic, bool bold, int y_offset); - +struct font *font_new(const char *name); struct font *font_clone(const struct font *font); void font_destroy(struct font *font); const char *font_face(const struct font *font); -int font_size(const struct font *font); -bool font_is_italic(const struct font *font); -bool font_is_bold(const struct font *font); -int font_y_offset(const struct font *font); - -cairo_scaled_font_t *font_use_in_cairo(const struct font *font, cairo_t *cr); +cairo_scaled_font_t *font_scaled_font(const struct font *font); diff --git a/particles/string.c b/particles/string.c index b1b3daa..303e50a 100644 --- a/particles/string.c +++ b/particles/string.c @@ -4,33 +4,46 @@ #include #include +#define LOG_MODULE "string" +#define LOG_ENABLE_DBG 1 +#include "../log.h" + struct private { char *text; + size_t max_len; struct font *font; struct rgba foreground; }; +struct eprivate { + char *text; + + const struct font *font; + struct rgba foreground; + + cairo_text_extents_t extents; +}; + static void exposable_destroy(struct exposable *exposable) { - struct private *e = exposable->private; + struct eprivate *e = exposable->private; free(e->text); free(e); exposable_default_destroy(exposable); } static int -begin_expose(struct exposable *exposable, cairo_t *cr) +begin_expose(struct exposable *exposable) { - const struct private *e = exposable->private; + struct eprivate *e = exposable->private; - cairo_scaled_font_t *scaled = font_use_in_cairo(e->font, cr); - cairo_text_extents_t extents; - cairo_scaled_font_text_extents(scaled, e->text, &extents); + cairo_scaled_font_t *scaled = font_scaled_font(e->font); + cairo_scaled_font_text_extents(scaled, e->text, &e->extents); exposable->width = (exposable->particle->left_margin + - extents.x_advance + + e->extents.x_advance + exposable->particle->right_margin); return exposable->width; @@ -39,12 +52,12 @@ begin_expose(struct exposable *exposable, cairo_t *cr) static void expose(const struct exposable *exposable, cairo_t *cr, int x, int y, int height) { - const struct private *e = exposable->private; + exposable_render_deco(exposable, cr, x, y, height); - cairo_scaled_font_t *scaled = font_use_in_cairo(e->font, cr); + const struct eprivate *e = exposable->private; + const size_t text_len = strlen(e->text); - cairo_text_extents_t extents; - cairo_scaled_font_text_extents(scaled, e->text, &extents); + cairo_scaled_font_t *scaled = font_scaled_font(e->font); cairo_glyph_t *glyphs = NULL; cairo_text_cluster_t *clusters = NULL; @@ -53,37 +66,47 @@ expose(const struct exposable *exposable, cairo_t *cr, int x, int y, int height) cairo_scaled_font_text_to_glyphs( scaled, x + exposable->particle->left_margin, - (double)y + ((double)height - extents.y_bearing) / 2 + font_y_offset(e->font), - e->text, strlen(e->text), &glyphs, &num_glyphs, + (double)y + ((double)height - e->extents.height) / 2 - e->extents.y_bearing, + e->text, text_len, &glyphs, &num_glyphs, &clusters, &num_clusters, &cluster_flags); - exposable_render_deco(exposable, cr, x, y, height); - + cairo_set_scaled_font(cr, scaled); cairo_set_source_rgba(cr, e->foreground.red, e->foreground.green, e->foreground.blue, e->foreground.alpha); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_show_text_glyphs(cr, e->text, strlen(e->text), - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags); + + cairo_show_text_glyphs( + cr, e->text, text_len, glyphs, num_glyphs, + clusters, num_clusters, cluster_flags); + cairo_glyph_free(glyphs); cairo_text_cluster_free(clusters); - /*cairo_scaled_font_destroy(scaled);*/ - } static struct exposable * instantiate(const struct particle *particle, const struct tag_set *tags) { const struct private *p = particle->private; - struct private *e = malloc(sizeof(*e)); + struct eprivate *e = malloc(sizeof(*e)); e->text = tags_expand_template(p->text, tags); e->font = p->font; e->foreground = p->foreground; + memset(&e->extents, 0, sizeof(e->extents)); + + if (p->max_len > 0) { + size_t len = strlen(e->text); + if (len > p->max_len) { + if (p->max_len >= 3) { + for (size_t i = 0; i < 3; i++) + e->text[p->max_len - 3 + i] = '.'; + } + e->text[p->max_len] = '\0'; + } + } char *on_click = tags_expand_template(particle->on_click_template, tags); @@ -108,12 +131,13 @@ particle_destroy(struct particle *particle) } struct particle * -particle_string_new(const char *text, struct font *font, +particle_string_new(const char *text, size_t max_len, struct font *font, struct rgba foreground, int left_margin, int right_margin, const char *on_click_template) { struct private *p = malloc(sizeof(*p)); p->text = strdup(text); + p->max_len = max_len; p->font = font; p->foreground = foreground; diff --git a/particles/string.h b/particles/string.h index 570b1ae..2f51ccb 100644 --- a/particles/string.h +++ b/particles/string.h @@ -2,5 +2,5 @@ #include "../particle.h" struct particle *particle_string_new( - const char *text, struct font *font, struct rgba foreground, + const char *text, size_t max_len, struct font *font, struct rgba foreground, int left_margin, int right_margin, const char *on_click_template);