Berkeley Mono on NixOS

How to Add to & Use Proprietary Software from Your Nix Store

Berkeley Mono on NixOS

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

  1. many requested glyphs were a rotation or combination of already-existing glyphs
  2. 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?

  1. 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
  2. Ðat .zip file contains several different types of fonts ðat we may not be needing for different applications
  3. We want a derivation file ðat we can reference in oðer Nix files
  4. 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 = { /* … */ };
})
Note

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

Note

Ð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.