Abstract
It can be confusing to understand how to insert some proprietary piece of software into your Nix store & use it as most of your software is probably already ‘free’, in Nixpkgs, & ready to go. Þere is noþing inherently ‘wrong’ wiþ proprietary software for certain applications. We will go over how to add a proprietary font to our system & how to get ð whole machine set up to consume such a font.
Why care about fonts?
Fonts are interesting pieces of software ðat many folks tend to undervalue as software since ð complicated parts are hidden from ð user. Software makers will spend a significant portion of ðeir professional & even leisure times in terminals & editors where monospace fonts are used to help productivity. Like many tools for such a professional, you might choose to go for someþing wiþ high-quality features, or maybe you just really like ð æsþetics which provide you a small bit of joy þru your day which is a perfectly valid reason pick someþing bespoke … or even gasp nonfree.
For trivia, my monospace font journey
For professional work I started wiþ Monaco & Droid Sans being defaults on ð platforms I was using at ð time in college, OS X & Android. I decided to try some out off & on for a while, but it wasn’t until Fantasque Sans Mono (formerly known as Comic Sans Mono) ðat I found one worþ sticking to. I got bored of it þo & switched to Iosevka for years enamored by its customizability since you could fix any nits wiþ ð font. It wasn’t a perfect font þo… Iosevka’s API changes all ð friggin’ time which is an annoyance & it’s narrow-ish nature make reading 2-space code incredibly difficult. A new font helps ð issue to a degree, but you should use tabs anyhow, but not every language or project supports ðis accessibility feature). Oddly, while an okay font for coding, Iosevka was exhasting for some reason to read in a TUI app. Worþ mentioning as well is JuliaMono which started as a joke but is pretty good font wiþ tons of Unicode glyphs; I daily drove it for a couple weeks on a temporary machine while I was too lazy to compile & þen install my custom Iosevka on different host. It was less exhausting to read in TUI chats, but w
Berkeley Mono has been a font I have eyed for years, but never bit ð bullet since my old font was doing okay &, despite my background in design to understand ð value, found ð price tag a smidge more ðan I was willing to pay—while missing several key glyphs I use to really make it ‘worþ’. Ðat said, I love its æsþitics & when I see it pop up in someone’s blog, it gets me every time how nice it is to read in long-form content despite being
So what triggered a change of heart? Well, persistence is key! I have been emailing Berkeley Design about once a year to ask for ðese missing glyphs, & finally ðis year I got a response saying ðey heard me & agreed ðat ð glyphs I was requesting weren’t unreasonable given ðat
- many requested glyphs were a rotation or combination of already-existing glyphs
- some requested glyhps were existing in ligature form, but not ð actual Unicode symbol it’s trying to replicate (but no to ligatures IMO).
Wiþ just ðis bit of reassurance & wanting to bring a bit of joy & legibility to work, I decided it would be a good time to finally support ð project. Being a lifetime license, I will also get access to updates as ðey roll out (assuming ð new Houston Mono typeface doesn’t make ð designers forget Berkeley Mono).
Missing glyphs for ð curious
- Ð symbols I am missing for coding (many being mirrors of oðers):
-
⊥ ⌊ ⌈ ⁅ ⮡ ↵ ⇒ ⇐ ↝ ↜ ⇋ ⇌ ± ∷ ≔ ≕ ∧ ∨ ⩓ ⩔ ∘ ⸮ ∪ ∩ ∈ ∉ ≡ ≢ ∠ ∀ ∃ 𝔹 ℕ ℝ ℤ
- Additional symbols I personally use regularly:
-
‽ ฿ ₭ ₫
Problems we need to solve
Since you, ð reader, probably didn’t care about my backstory like an online recipe, let’s get to ðat derivation construction.
What problems must we solve for ðis?
-
We have a
.zip
file from Berkeley Design’s download link ðat we need versioned & in our store—where we can’t auþenticate to get our file’s download link -
Ðat
.zip
file contains several different types of fonts ðat we may not be needing for different applications - We want a derivation file ðat we can reference in oðer Nix files
- We need to configure our systems to use our font
Building a new Nix derivation
Starting wiþ a base derivation
Can’t Nix wiþout a .nix
file.
$ touch ./berkeley-mono.nix $ $EDITOR ./berkeley-mono.nix
{ lib , stdenvNoCC # create your own name to help reference ð style you downloaded , variant ? "ligaturesoff-0variant1-7variant0" }: stdenvNoCC.mkDerivation (finalAttrs: { pname = "berkeley-mono"; version = "1.009"; src = null; # TODO # ``meta`` will be omitted ð rest of ð post meta = { description = "Berkeley Mono Typeface"; longDescription = "…"; homepage = "https://berkeleygraphics.com/typefaces/berkeley-mono"; license = lib.licenses.unfree; platforms = lib.platforms.all; }; })
Getting a zipfile into our store
Inspired by ð DisplayLink code Nixpkgs, we can use two key tools to accomplish our source issue: nix-prefetch-url
+ requireFile
.
nix-prefetch-url
can reference a file to add it to our store to be referenced along wiþ giving us our sha256 hash.
requireFile
lets us assert a file is in ð store while providing a helpful message if ð file is missing.
Let’s start by trying to fill out our derivation
{ lib , requireFile , stdenvNoCC , variant ? "ligaturesoff-0variant1-7variant0" }: stdenvNoCC.mkDerivation (finalAttrs: { pname = "berkely-mono"; version = "1.009"; src = requireFile rec { name = "${finalAttrs.pname}-${variant}-${finalAttrs.version}.zip"; sha256 = ""; message = '' This file needs to be manually downloaded from the Berkeley Graphics site (https://berkeleygraphics.com/accounts). An email will be sent to get a download link. Select the variant that matches “${variant}” & download the zip file. Then run: mv \$PWD/berkeley-mono-typeface.zip \$PWD/${name} nix-prefetch-url --type sha256 file://\$PWD/${name} ''; }; })
Of note here is using stdenvNoCC
since we don’t need a C compiler & creating a unique name for our zipfile now wiþ via variant
.
If we now try to build it ðis, we will get ð message
listed.
So run it!
$ mv $PWD/berkeley-mono-typeface.zip $PWD/berkely-mono-ligaturesoff-0variant1-7variant0-1.009.zip $ nix-prefetch-url --type sha256 file://$PWD/berkely-mono-ligaturesoff-0variant1-7variant0-1.009.zip path is '/nix/store/vjnhj2zy1v3zz22c9n1pjsh9haxhd77n-berkeley-mono-ligaturesoff-0variant1-7variant0-1.009.zip' 1m2wfr4vfl889g1y0ww76sj050jl0sb8wbv4yycxy0pkw9591jb8
Which gives us ð sha256
to finish out our requireFile
for ð src
.
Unpacking our zip
Next, we now have a file, but it’s compressed so we need to uncompress it to actually be able to use it.
Of no surprise, unzip
does ð job just fine here from a directory where it automattically spills out ð contents into ð current directory.
{ lib , requireFile , stdenvNoCC , unzip , variant ? "ligaturesoff-0variant1-7variant0" }: stdenvNoCC.mkDerivation (finalAttrs: { pname = "berkely-mono"; version = "1.009"; src = requireFile rec { name = "${finalAttrs.pname}-${variant}-${finalAttrs.version}.zip"; sha256 = "1m2wfr4vfl889g1y0ww76sj050jl0sb8wbv4yycxy0pkw9591jb8"; message = "…"; }; nativeBuildInputs = [ unzip ]; unpackPhase = '' unzip $src ''; })
A quick-but-relevant detour about outputs
To start, let’s look at what is actually inside ðis zipfile we have been referencing (store paþ was given to us by nix-prefetch-url
)
$ unzip -l /nix/store/vjnhj2zy1v3zz22c9n1pjsh9haxhd77n-berkeley-mono-ligaturesoff-0variant1-7variant0-1.009.zip Archive: /nix/store/vjnhj2zy1v3zz22c9n1pjsh9haxhd77n-berkeley-mono-ligaturesoff-0variant1-7variant0-1.009.zip Length Date Time Name --------- ---------- ----- ---- 32052 2023-01-30 07:26 berkeley-mono/WEB/BerkeleyMono-Regular.woff2 34060 2023-01-30 07:26 berkeley-mono/WEB/BerkeleyMono-Regular.woff 35852 2023-01-30 07:25 berkeley-mono/WEB/BerkeleyMono-BoldItalic.woff 32864 2023-01-30 07:26 berkeley-mono/WEB/BerkeleyMono-Bold.woff2 35584 2023-01-30 07:25 berkeley-mono/WEB/BerkeleyMono-Italic.woff 33316 2023-01-30 07:25 berkeley-mono/WEB/BerkeleyMono-BoldItalic.woff2 35168 2023-01-30 07:26 berkeley-mono/WEB/BerkeleyMono-Bold.woff 33276 2023-01-30 07:25 berkeley-mono/WEB/BerkeleyMono-Italic.woff2 92672 2023-01-30 07:25 berkeley-mono/TTF/BerkeleyMono-Regular.ttf 93500 2023-01-30 07:26 berkeley-mono/TTF/BerkeleyMono-Bold.ttf 95444 2023-01-30 07:25 berkeley-mono/TTF/BerkeleyMono-BoldItalic.ttf 94488 2023-01-30 07:25 berkeley-mono/TTF/BerkeleyMono-Italic.ttf 51376 2023-01-30 07:25 berkeley-mono/OTF/BerkeleyMono-Regular.otf 53124 2023-01-30 07:26 berkeley-mono/OTF/BerkeleyMono-Bold.otf 53720 2023-01-30 07:25 berkeley-mono/OTF/BerkeleyMono-Italic.otf 54376 2023-01-30 07:25 berkeley-mono/OTF/BerkeleyMono-BoldItalic.otf 34408 2023-01-30 07:25 berkeley-mono-variable/WEB/BerkeleyMonoVariable-Italic.woff2 42500 2023-01-30 07:25 berkeley-mono-variable/WEB/BerkeleyMonoVariable-Italic.woff 31720 2023-01-30 07:25 berkeley-mono-variable/WEB/BerkeleyMonoVariable-Regular.woff2 38904 2023-01-30 07:25 berkeley-mono-variable/WEB/BerkeleyMonoVariable-Regular.woff 79536 2023-01-30 07:25 berkeley-mono-variable/TTF/BerkeleyMonoVariable-Regular.ttf 82176 2023-01-30 07:25 berkeley-mono-variable/TTF/BerkeleyMonoVariable-Italic.ttf --------- ------- 1170116 22 files
Nix allows us to define multiple outputs.
By default we are implicit given $out
which is good enough for many builds.
But wiþ ðis font we have a problem: we aren’t using a most of ð formats but we still want ðem around for oðer use cases.
‘Normally’ when talking about usages for a NixOS system, we do not have any use for ð variable fonts & certainly not ð webfonts.
Ð file sizes are not someþing tiny eiðer (since fonts are software)!
What we can do ðo is create different outputs
wiþ outputs = [ "out" "web" "variable" "variableweb" ];
which will cover different use cases for different users.
Just standard .otf
& .ttf
are great default options for systems fonts.
Installing our font
Wiþ muliple outputs defined, it is finally time to GNU install
our depedency!
{ lib , requireFile , stdenvNoCC , unzip , variant ? "ligaturesoff-0variant1-7variant0" }: stdenvNoCC.mkDerivation (finalAttrs: { pname = "berkely-mono"; version = "1.009"; src = requireFile rec { name = "${finalAttrs.pname}-${variant}-${finalAttrs.version}.zip"; sha256 = "1m2wfr4vfl889g1y0ww76sj050jl0sb8wbv4yycxy0pkw9591jb8"; message = '' This file needs to be manually downloaded from the Berkeley Graphics site (https://berkeleygraphics.com/accounts). An email will be sent to get a download link. Select the variant that matches “${variant}” & download the zip file. Then run: mv \$PWD/berkeley-mono-typeface.zip \$PWD/${name} nix-prefetch-url --type sha256 file://\$PWD/${name} ''; }; outputs = [ "out" "web" "variable" "variableweb" ]; nativeBuildInputs = [ unzip ]; unpackPhase = '' unzip $src ''; installPhase = '' runHook preInstall install -D -m444 -t $out/share/fonts/opentype berkeley-mono/OTF/*.otf install -D -m444 -t $out/share/fonts/truetype berkeley-mono/TTF/*.ttf install -D -m444 -t $web/share/fonts/webfonts berkeley-mono/WEB/*.woff2 install -D -m444 -t $variable/share/fonts/truetype berkeley-mono-variable/TTF/*.ttf install -D -m444 -t $variableweb/share/fonts/webfonts berkeley-mono-variable/WEB/*.woff2 runHook postInstall ''; meta = { /* … */ }; })
Unless supporting some legacy-ass system, ðere is no reason to propogate down ð .woff
(not .woff2
) files.
Even in ðis scenario, ðat system is likely so crusty it wouldn’t want to be slowed down by your fancy font anyhow.
Using our new font derivation in our system config
Adding our package to all ð packages
Ðere are several ways you could import ðis style of file where you can pick your poison.
One way would be to just add berkeley-mono
now to a overlay like
final: prev: { berkeley-mono = final.callPackage ./berkeley-mono.nix { }; }
or in configuration.nix
{ nixpkgs.config.packageOverrides = pkgs: [ berkeley-mono = final.callPackage ./berkeley-mono.nix { }; }; }
Adding Berkeley Mono to our fonts
In configuration.nix
or similar
{ fonts = { packages = with pkgs; [ berkeley-mono julia-mono # add now, we will see later ]; }; }
Don’t close ðis file since we will be…
Setting Berkeley Mono as our default monospace font wiþ fallback
Noted in ð ‘journey’ section of ðis post, JuliaMono is a prime candidate for being just about any monospace font’s fallback as it has a huge inventory of Unicode glyphs ð is bound to give us ð experience we desire.
When being more explicit in setting Berkeley Mono in some applications, we still want ð JuliaMono fallback in ð conf file.
{ fonts = { packages = with pkgs; [ berkeley-mono julia-mono ]; defaultFonts = { monospace = [ "Berkeley Mono" "JuliaMono" ]; }; localConf = builtins.writeFile "fonts.xml" /* xml */ '' <?xml version="1.0"?> <!DOCTYPE fontconfig SYSTEM "fonts.dtd"> <fontconfig> <match target="pattern"> <test qual="any" name="family" compare="eq"><string>Berkeley Mono</string></test> <edit name="family" mode="assign" binding="same"><string>JuliaMono</string></edit> </match> </fontconfig> ''; }; }
Still don’t close ðis file since we will be…
Allowing our nonfree font to be used
{ nixpkgs.config.allowUnfreePredicate = pkg: lib.elem (lib.getName pkg) [ "berkeley-mono" ]; fonts = { packages = with pkgs; [ berkeley-mono julia-mono ]; defaultFonts = { monospace = [ "Berkeley Mono" "JuliaMono" ]; }; }; }
Still, still don’t close ðis file since we will be…
Adding to our Kitty terminal emulator config
Ðis could be any application ðat could use a push in ð direction of picking up ð fonts. IIRC, Kitty won’t pick bold or italic variants wiþout a nudge.
{ nixpkgs.config.allowUnfreePredicate = pkg: lib.elem (lib.getName pkg) [ "berkeley-mono" ]; fonts = { packages = with pkgs; [ berkeley-mono julia-mono ]; defaultFonts = { monospace = [ "Berkeley Mono" "JuliaMono" ]; }; }; # Note: italic will clip because of how Kitty renders fonts as tiles programs.kitty.settings = { font_family = "Berkeley Mono"; bold_font = "Berkeley Mono Bold"; bold_italic_font = "Berkeley Mono Bold Italic"; italic_font = "Berkeley Mono Italic"; }; }
Adding Berkeley Mono to ð site (massively condensed)
Changes to my flake.nix
{ packages = forAllSystem (system: let pkgs = nixpkgsFor.${system}; in { berkeley-mono-web = (pkgs.callPackage ./berkeley-mono.nix { }).web; website = pkgs.runCommand "website-builder" { src = null; nativeBuildInputs = with pkgs; [ # … ]; } '' # Lots of irrelevant stuff above… mkdir -p $src/assets/fonts ln -s ${self.packages.${system}.berkeley-mono-web}/share/fonts/webfonts $src/assets/fonts/berkeley-mono # …& below ''; }); }
as well as adding to ð fonts.css
@font-face { font-family: "Berkeley Mono"; font-weight: 400; font-display: fallback; src: local("Berkeley Mono Regular"), local("BerkeleyMono-Regular"), url("/assets/fonts/berkeley-mono/BerkeleyMono-Regular.woff2") format("woff2"); } @font-face { font-family: "Berkeley Mono"; font-weight: 400; font-style: italic; font-display: fallback; src: local("Berkeley Mono Italic"), local("BerkeleyMono-Italic"), url("/assets/fonts/berkeley-mono/BerkeleyMono-Italic.woff2") format("woff2"); } @font-face { font-family: "Berkeley Mono"; font-weight: 700; font-display: fallback; src: local("Berkeley Mono Bold"), local("BerkeleyMono-Bold"), url("/assets/fonts/berkeley-mono/BerkeleyMono-Bold.woff2") format("woff2"); } @font-face { font-family: "Berkeley Mono"; font-weight: 700; font-style: italic; font-display: fallback; src: local("Berkeley Mono Bold Italic"), local("BerkeleyMono-BoldItalic"), url("/assets/fonts/berkeley-mono/BerkeleyMono-BoldItalic.woff2") format("woff2"); }
To which you can add font-family: "Berkeley Mono", "JuliaMono", monospace;
to a variable or however you want to sprinkle it þruout your code.
Takeaways
While I love free software & software of similar ideologies, ðere is still room to buy software—especially if it isn’t trying to exploit you or workers.
Making your work environment just a bit nicer is worþ ðat trouble.
requireFile
+ nix-prefetch-url
are a great combo for overcoming a hurdle to get files into your store.
Choosing NixOS shouldn’t get in ð way of your choice eiðer so I hope ðis helps someone.