diff options
-rw-r--r-- | completions/tofi | 3 | ||||
-rw-r--r-- | doc/config | 12 | ||||
-rw-r--r-- | doc/tofi.5.md | 9 | ||||
-rw-r--r-- | doc/tofi.5.scd | 8 | ||||
-rw-r--r-- | meson.build | 7 | ||||
-rw-r--r-- | src/config.c | 2 | ||||
-rw-r--r-- | src/entry.c | 1 | ||||
-rw-r--r-- | src/entry.h | 4 | ||||
-rw-r--r-- | src/entry_backend/harfbuzz.c | 83 | ||||
-rw-r--r-- | src/entry_backend/harfbuzz.h | 3 | ||||
-rw-r--r-- | src/entry_backend/pango.c | 5 | ||||
-rw-r--r-- | src/main.c | 8 |
12 files changed, 120 insertions, 25 deletions
diff --git a/completions/tofi b/completions/tofi index ca19509..0e50af2 100644 --- a/completions/tofi +++ b/completions/tofi @@ -16,6 +16,8 @@ _tofi() --corner-radius --font --font-size + --font-features + --font-variations --num-results --selection-color --selection-match-color @@ -54,7 +56,6 @@ _tofi() --drun-launch --terminal --hint-font - --font-features --late-keyboard-init --multi-instance ) @@ -25,9 +25,19 @@ # Examples: # # font-features = "smcp, c2sc" (all small caps) - # font-features = "liga 0" (disable ligatures + # font-features = "liga 0" (disable ligatures) font-features = "" + # Comma separated list of OpenType font variation settings to apply + # to variable fonts. The format is similar to the CSS + # "font-variation-settings" property. + # + # Examples: + # + # font-variations = "wght 900" (Extra bold) + # font-variations = "wdth 25, slnt -10" (Narrow and slanted) + font-variations = "" + # Perform font hinting. Only applies when a path to a font has been # specified via `font`. Disabling font hinting speeds up text # rendering appreciably, but will likely look poor at small font pixel diff --git a/doc/tofi.5.md b/doc/tofi.5.md index dea750c..e5f947b 100644 --- a/doc/tofi.5.md +++ b/doc/tofi.5.md @@ -165,6 +165,15 @@ options. > > Default: "" +**font-variations**=*variations* + +> Comma separated list of OpenType font variation settings to apply. The +> format is similar to the CSS "font-variation-settings" property. For +> example, "wght 900" will set the weight of a variable font to 900 (if +> supported by the chosen font). +> +> Default: "" + **background-color**=*color* > Color of the background. See **COLORS** for more information. diff --git a/doc/tofi.5.scd b/doc/tofi.5.scd index 9ff7f3d..acdb16a 100644 --- a/doc/tofi.5.scd +++ b/doc/tofi.5.scd @@ -143,6 +143,14 @@ options. Default: "" +*font-variations*=_variations_ + Comma separated list of OpenType font variation settings to apply. The + format is similar to the CSS "font-variation-settings" property. For + example, "wght 900" will set the weight of a variable font to 900 (if + supported by the chosen font). + + Default: "" + *background-color*=_color_ Color of the background. See *COLORS* for more information. diff --git a/meson.build b/meson.build index 3071aed..ae49ba5 100644 --- a/meson.build +++ b/meson.build @@ -147,6 +147,13 @@ if wayland_client.version().version_compare('<1.20.0') ) endif +if harfbuzz.version().version_compare('<4.4.0') + add_project_arguments( + ['-DNO_HARFBUZZ_FONT_CHANGED=1'], + language: 'c' + ) +endif + # Generate the necessary Wayland headers / sources with wayland-scanner diff --git a/src/config.c b/src/config.c index 6655f00..3a65d3d 100644 --- a/src/config.c +++ b/src/config.c @@ -327,6 +327,8 @@ bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const tofi->window.entry.font_size = parse_uint32(filename, lineno, value, &err); } else if (strcasecmp(option, "font-features") == 0) { snprintf(tofi->window.entry.font_features, N_ELEM(tofi->window.entry.font_features), "%s", value); + } else if (strcasecmp(option, "font-variations") == 0) { + snprintf(tofi->window.entry.font_variations, N_ELEM(tofi->window.entry.font_variations), "%s", value); } else if (strcasecmp(option, "num-results") == 0) { tofi->window.entry.num_results = parse_uint32(filename, lineno, value, &err); } else if (strcasecmp(option, "outline-width") == 0) { diff --git a/src/entry.c b/src/entry.c index 3d0c284..70f4525 100644 --- a/src/entry.c +++ b/src/entry.c @@ -63,6 +63,7 @@ void entry_init(struct entry *entry, uint8_t *restrict buffer, uint32_t width, u entry->cairo[1].cr = cairo_create(entry->cairo[1].surface); + log_debug("Drawing window.\n"); /* Draw the background */ struct color color = entry->background_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); diff --git a/src/entry.h b/src/entry.h index fb8e6c7..8e9d2b4 100644 --- a/src/entry.h +++ b/src/entry.h @@ -16,7 +16,8 @@ #define MAX_INPUT_LENGTH 256 #define MAX_PROMPT_LENGTH 256 #define MAX_FONT_NAME_LENGTH 256 -#define MAX_FONT_FEATURES_LENGTH 256 +#define MAX_FONT_FEATURES_LENGTH 128 +#define MAX_FONT_VARIATIONS_LENGTH 128 struct entry { struct image image; @@ -59,6 +60,7 @@ struct entry { uint32_t font_size; char font_name[MAX_FONT_NAME_LENGTH]; char font_features[MAX_FONT_FEATURES_LENGTH]; + char font_variations[MAX_FONT_VARIATIONS_LENGTH]; char prompt_text[MAX_PROMPT_LENGTH]; char placeholder_text[MAX_PROMPT_LENGTH]; uint32_t prompt_padding; diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c index 53b1740..f038578 100644 --- a/src/entry_backend/harfbuzz.c +++ b/src/entry_backend/harfbuzz.c @@ -147,6 +147,23 @@ void entry_backend_harfbuzz_init( cairo_t *cr = entry->cairo[0].cr; uint32_t font_size = floor(entry->font_size * PT_TO_DPI); + /* + * Setting up our font has three main steps: + * + * 1. Load the font face with FreeType. + * 2. Create a HarfBuzz font referencing the FreeType font. + * 3. Create a Cairo font referencing the FreeType font. + * + * The simultaneous interaction of Cairo and HarfBuzz with FreeType is + * a little finicky, so the order of the last two steps is important. + * We use HarfBuzz to set font variation settings (such as weight), if + * any. This modifies the underlying FreeType font, so we must create + * the Cairo font *after* this point for the changes to take effect. + * + * This doesn't seem like it should be necessary, as both HarfBuzz and + * Cairo reference the same FreeType font, but it is. + */ + /* Setup FreeType. */ log_debug("Creating FreeType library.\n"); int err; @@ -167,6 +184,7 @@ void entry_backend_harfbuzz_init( log_error("Error loading font: %s\n", get_ft_error_string(err)); exit(EXIT_FAILURE); } + err = FT_Set_Char_Size( hb->ft_face, font_size * 64, @@ -178,6 +196,49 @@ void entry_backend_harfbuzz_init( get_ft_error_string(err)); } + log_debug("Creating Harfbuzz font.\n"); + hb->hb_font = hb_ft_font_create_referenced(hb->ft_face); + + if (entry->font_variations[0] != 0) { + log_debug("Parsing font variations.\n"); + } + char *saveptr = NULL; + char *variation = strtok_r(entry->font_variations, ",", &saveptr); + while (variation != NULL && hb->num_variations < N_ELEM(hb->hb_variations)) { + if (hb_variation_from_string(variation, -1, &hb->hb_variations[hb->num_variations])) { + hb->num_variations++; + } else { + log_error("Failed to parse font variation \"%s\".\n", variation); + } + variation = strtok_r(NULL, ",", &saveptr); + } + + /* + * We need to set variations now and update the underlying FreeType + * font, as Cairo will then use the FreeType font for drawing. + */ + hb_font_set_variations(hb->hb_font, hb->hb_variations, hb->num_variations); +#ifndef NO_HARFBUZZ_FONT_CHANGED + hb_ft_hb_font_changed(hb->hb_font); +#endif + + if (entry->font_features[0] != 0) { + log_debug("Parsing font features.\n"); + } + saveptr = NULL; + char *feature = strtok_r(entry->font_features, ",", &saveptr); + while (feature != NULL && hb->num_features < N_ELEM(hb->hb_features)) { + if (hb_feature_from_string(feature, -1, &hb->hb_features[hb->num_features])) { + hb->num_features++; + } else { + log_error("Failed to parse font feature \"%s\".\n", feature); + } + feature = strtok_r(NULL, ",", &saveptr); + } + + log_debug("Creating Harfbuzz buffer.\n"); + hb->hb_buffer = hb_buffer_create(); + log_debug("Creating Cairo font.\n"); hb->cairo_face = cairo_ft_font_face_create_for_ft_face(hb->ft_face, 0); @@ -193,34 +254,12 @@ void entry_backend_harfbuzz_init( } cairo_set_font_options(cr, opts); - /* We also need to set up the font for our other Cairo context. */ cairo_set_font_face(entry->cairo[1].cr, hb->cairo_face); cairo_set_font_size(entry->cairo[1].cr, font_size); cairo_set_font_options(entry->cairo[1].cr, opts); cairo_font_options_destroy(opts); - - log_debug("Creating Harfbuzz font.\n"); - hb->hb_font = hb_ft_font_create_referenced(hb->ft_face); - - log_debug("Creating Harfbuzz buffer.\n"); - hb_buffer_t *buffer = hb_buffer_create(); - hb->hb_buffer = buffer; - setup_hb_buffer(buffer); - - - log_debug("Parsing font features.\n"); - char *saveptr = NULL; - char *feature = strtok_r(entry->font_features, ",", &saveptr); - while (feature != NULL && hb->num_features < N_ELEM(hb->hb_features)) { - if (hb_feature_from_string(feature, -1, &hb->hb_features[hb->num_features])) { - hb->num_features++; - } else { - log_error("Failed to parse font feature \"%s\".\n", feature); - } - feature = strtok_r(NULL, ",", &saveptr); - } } void entry_backend_harfbuzz_destroy(struct entry *entry) diff --git a/src/entry_backend/harfbuzz.h b/src/entry_backend/harfbuzz.h index 4c45195..17b5945 100644 --- a/src/entry_backend/harfbuzz.h +++ b/src/entry_backend/harfbuzz.h @@ -7,6 +7,7 @@ #include FT_FREETYPE_H #include <harfbuzz/hb.h> +#define MAX_FONT_VARIATIONS 16 #define MAX_FONT_FEATURES 16 struct entry; @@ -19,7 +20,9 @@ struct entry_backend_harfbuzz { hb_font_t *hb_font; hb_buffer_t *hb_buffer; + hb_variation_t hb_variations[MAX_FONT_VARIATIONS]; hb_feature_t hb_features[MAX_FONT_FEATURES]; + uint8_t num_variations; uint8_t num_features; bool disable_hinting; diff --git a/src/entry_backend/pango.c b/src/entry_backend/pango.c index be3ccdb..7a63d1a 100644 --- a/src/entry_backend/pango.c +++ b/src/entry_backend/pango.c @@ -27,6 +27,11 @@ void entry_backend_pango_init(struct entry *entry, uint32_t *width, uint32_t *he pango_font_description_set_size( font_description, entry->font_size * PANGO_SCALE); + if (entry->font_variations[0] != 0) { + pango_font_description_set_variations( + font_description, + entry->font_variations); + } pango_context_set_font_description(context, font_description); pango_font_description_free(font_description); @@ -754,6 +754,10 @@ static const struct wl_surface_listener dummy_surface_listener = { static void usage() { + /* + * This string literal is more than 4095 characters, which is the + * maximum size guaranteed to work in C, so we have to split it. + */ fprintf(stderr, "%s", "Usage: tofi [options]\n" "\n" @@ -763,6 +767,7 @@ static void usage() " --font <name|path> Font to use.\n" " --font-size <pt> Point size of text.\n" " --font-features <features> Set OpenType font features.\n" +" --font-variations <variations> Set OpenType font variations.\n" " --background-color <color> Color of the background.\n" " --outline-width <px> Width of the border outlines.\n" " --outline-color <color> Color of the border outlines.\n" @@ -783,6 +788,8 @@ static void usage() " --result-spacing <px> Spacing between results.\n" " --min-input-width <px> Minimum input width in horizontal mode.\n" " --width <px|%> Width of the window.\n" + ); + fprintf(stderr, "%s", " --height <px|%> Height of the window.\n" " --corner-radius <px> Radius of window corners.\n" " --output <name> Name of output to display window on.\n" @@ -831,6 +838,7 @@ const struct option long_options[] = { {"font", required_argument, NULL, 0}, {"font-size", required_argument, NULL, 0}, {"font-features", required_argument, NULL, 0}, + {"font-variations", required_argument, NULL, 0}, {"num-results", required_argument, NULL, 0}, {"selection-color", required_argument, NULL, 0}, {"selection-match-color", required_argument, NULL, 0}, |