Liang-Bo Wang's Blog

About | Talks | Archives |

Fix Fira Code font ligatures and features

Fira Code has been my choice of the programming font for a while. It’s also the default monospace font of my blog. I like its ligatures such as >= and connected lines ====== ------. It evens renders the progress bar nicely . It makes my plain text documents look neat.

That said, I don’t like the default ampersands & and the at signs @. I find them harder to read than their traditional looks. To change their looks, we can enable the alternative ligatures and features of the font using different OpenType features (see also the guide on MDN). In this case, ss05 and ss08 enable the traditional looks of ampersands and at signs, respectively. Most modern editors and word processors are able to configure the feature sets in use.

Comparison of the Fira Code rendering with and without the features fixed (ss01, ss03, ss05, and ss08).

Unfortunately, I encounter programs that are unable to configure the font features. While tools like pyftfeatfreeze (OpenType Feature Freezer) are able to swap specific glyphs by directly editing the font file, ligatures of those glyphs may fail. For example, ss08 feature (e.g., ==, !=, and ===) won’t be permanently enabled using this approach.

Permanently fix the font features using the official build script (update in 2022-03)

Thanks to the pull request by @Daxtorim a few days after this post was published, we now are able to permanently fix the font features using the official build script:

docker run -it --rm \
    -v $PWD:/opt/FiraCode \
    tonsky/firacode:latest \
    ./FiraCode/script/build.sh \
    -f "ss01,ss03,ss05,ss08" \
    -n "Fira Code ss01 ss03 ss05 ss08"

# Rename the generated TTFs
parallel 'mv {} {.}.ss1358_enabled.ttf' ::: distr/ttf/'Fira Code ss01 ss03 ss05 ss08'/*.ttf

And that’s it! This is the easiest solution and it works straight out of the box. Praise the open source community :) I still kept the original instructions below to manually create the patches since that’s what happens behind the scene.

Permanently fix the font features

By changing the source code of the font generation, it should be possible to permanently fix any font features (aka patching). As mentioned by the original author (@tonsky):

There is probably a simpler approach to patching the font, just concat whatever code there is in ssXX and add it to the end of calt feature. That should work on the current version of the font, but you’ll need to do your own research on which scripts to use for that

And that’s exactly my patch for FiraCode.glyphs. Copy all the content of the features (say, ss01, ss03, ss05, and ss08) to calt. So the original code:

# FiraCode.glyphs
{
code = "lookup less_bar_greater ...
... underscores;\012";
name = calt;
},

becomes:

{
code = "lookup less_bar_greater ...
... underscores;\012
# ss01\012  sub r by r.ss01;
# ss03\012  sub ampersand by ampersand.ss03;...
# ss05\012  sub at by at.ss05;\012sub asciitilde.spacer'; ...
# ss08\012  sub equal_equal.liga by equal_equal.ss08;...
";
name = calt;
},

Then build the font from the .glyphs file using the Docker image:

docker run -it --rm \
    -v $PWD:/opt/FiraCode \
    tonsky/firacode ./FiraCode/script/build.sh

To set a different font name for the feature fixed fonts, I use pyftfeatfreeze:

parallel \
    pyftfeatfreeze \
        --suffix --usesuffix="'ss01 ss03 ss05 ss08'" \
        -v -n \
        '{}' 'features_enabled/{/.}.ss1358_enabled.ttf' \
        ::: ttf/*.ttf