LaTeX, fonts, & NixOS

Back in 2006, for reasons that made sense to me at the time, and make no sense to anyone at all now, I decided to buy some fonts and use them when writing my final-year thesis for university. I was writing my thesis in LaTeX. What followed was a week in which I read some pretty thorough documentation and slowly figured out how to convert fonts in normal TrueType and OpenType formats into the variety of formats that LaTeX needs, and how to install them. Having been through this pain once, I then decided to use these fonts and this setup for pretty much every document I would ever write from that point onwards. As I was already using Debian, whenever I set up a new machine, I would copy all these font files to the new machine, putting them in the same places as before, and everything Just Worked.

I’ve just left my previous job, so it’s probably time I updated my CV (Résumé). Which is in LaTeX, using these fonts, and I’ve just switched OS from Debian to NixOS. So in order to be able to build my CV, I need to figure out how LaTeX works in NixOS, and how to install my own fonts in it. Now although I’m sure the number of people still using LaTeX is high (as far as I know, it’s still the standard for many subjects in academia), I suspect the number of people using LaTeX in NixOS is rather low. And the number of people who have installed their own fonts is even lower. The reason I think this is because there was precious little documentation about how to do this, and whilst it turned out to not require a lot of code, it took a solid day to figure it all out. That said, I’m definitely still a NixOS novice. And yes, I realise this is all ridiculous.

So in an attempt to improve the documentation situation, here’s a guide. Kinda.

We’re going to be creating some Nix derivations that:

  • Make your TrueType and OpenType fonts available for the system as a whole so programs like libreoffice, chrome, inkscape etc can all find and use them.
  • Create a LaTeX derivation that combines your custom fonts (plus any .sty files you’ve created) with a LaTeX derivation from nixpkgs.

I assume you’ve already done everything necessary to get your fonts ready for LaTeX. If the whole Berry naming scheme and all that jazz is still A Thing then I trust you’ve found and followed the guides and got your font files prepared.

System-wide fonts

Because fonts need to be added to various databases, we need to add derivations into the system-wide fonts.fonts variable. So, in our /etc/nixos/configuration.nix file, we’re going to need something like this:

let
  myfonts = import /home/matthew/.dotfiles/myfonts.nix { inherit pkgs; };
in
{
  ...
  fonts.fonts = [ myfonts.fonts ];
  ...
}

You can guess that ~/.dotfiles is my repo for storing all my dotfiles and system configuration bits and bobs.

Firstly, let’s get the directory structure right. In my case, I’m installing MyriadPro which is from Adobe (and I have in OpenType format), and also Plantin, which is from Monotype (and I have in TrueType format). So:

# cd ~/.dotfiles
# mkdir -p myfonts/share/fonts/opentype/adobe/myriad/
# mkdir -p myfonts/share/fonts/truetype/monotype/plantin/

Populate those directories with the relevant files. Now, let’s start ~/.dotfiles/myfonts.nix:

{ pkgs ? import <nixpkgs> {} }:

let
  fonts = pkgs.stdenvNoCC.mkDerivation {
    pname = "myfonts";
    version = "1.0.0";
    src = ./myfonts;
    dontConfigure = true;

    installPhase = ''
      runHook preInstall
      cp -R share $out/
      runHook postInstall
    '';

    meta = {
      description = "Adobe Myriad and Monotype Plantin fonts";
    };
  };
in
{
  inherit fonts;
}

This file gets a bit bigger with time, which is why it’s set up the way it is, and returns attributes rather than a single derivation.

With these two changes done, we should be able to do a nixos-rebuild switch and now find our fonts are available to normal desktop programs.

LaTeX

LaTeX in NixOS is provided in a number of different schemes depending on quite how much of LaTeX (or rather TexLive) you want installed. I decided not to muck about and just install everything. So the basic derivation I’m trying to extend is pkgs.texlive.scheme-full. You may be aware that installing fonts into LaTeX requires:

  1. Putting all the font files in their various formats in the right place.
  2. Enabling your new font map files, and then running updmap --sys.

NixOS provides a pkgs.texlive.combine function that returns a derivation combining a bunch of LaTeX packages. That’s where we’ll start: we’ll provide our own package as an argument to combine. This will at least get all the files in the right place. Again, let’s begin by putting our LaTeX-suitable font files in the right layout for the source:

# cd ~/.dotfiles/myfonts
# mkdir -p tex/latex/adobe/myriad             # Put .sty and .fd files for Myriad in here
# mkdir -p tex/latex/monotype/plantin         # Similarly .sty and .fd files.
# mkdir -p share/fonts/vf/adobe/myriad        # For .vf files.
# mkdir -p share/fonts/vf/monotype/plantin
# mkdir -p share/fonts/type1/adobe/myriad     # For .type1 files.
# mkdir -p share/fonts/type1/monotype/plantin
# mkdir -p share/fonts/afm/adobe/myriad       # For .afm files.
# mkdir -p share/fonts/afm/monotype/plantin
# mkdir -p share/fonts/tfm/adobe/myriad       # For .tfm files.
# mkdir -p share/fonts/tfm/monotype/plantin
# mkdir -p share/fonts/map/dvips/myriad       # For .map files.
# mkdir -p share/fonts/map/dvips/plantin

Keep the myfonts/share/fonts/opentype and myfonts/share/fonts/truetype directories from the previous steps unaltered.

With those directories all populated, we want to extend our myfonts.nix file:

{ pkgs ? import <nixpkgs> {} }:

let
  fonts = pkgs.stdenvNoCC.mkDerivation {
    pname = "myfonts";
    version = "1.0.0";
    passthru.tlType = "run";
    src = ./myfonts;
    dontConfigure = true;

    installPhase = ''
      runHook preInstall
      cp -R share $out/
      cp -R tex $out/
      runHook postInstall
    '';

    meta = {
      description = "Adobe Myriad and Monotype Plantin fonts";
    };
  };

  latexFonts = { pkgs = [ fonts ]; };

  mytexlive = (pkgs.texlive.combine {
    inherit (pkgs.texlive)
      scheme-full;
    inherit latexFonts;
  });
in
{
  inherit fonts mytexlive;
}

A few things to note:

  • We’re copying both tex and share now to $out: our single fonts derivation will work both to provide font files to LaTeX and to the system as a whole.
  • We’ve added passthru.tlType - I guess tl stands for TexLive. I’m honestly not certain about the "run" value, but I had a quick read through some of the TexLive derivations in nixpkgs and the only other value I could see was "bin" which then went into code that was doing wrapping of binaries and such like. So "run" seems right.
  • The latexFonts wrapping seems to be something that’s just required to present the package in the right way to combine.
  • mytexlive is the result of combining the provided pkgs.texlive.scheme-full derivation with our custom fonts derivation.

(Update: An earlier version of this post had both name and pname as attributes in the derivation. I was confused. I now believe that the “right” thing to do is always have pname and version and never worry much about name.)

So we should now be able to build this:

# cd ~/.dotfiles
# nix-build -A mytexlive myfonts.nix

If we look inside the ./result directory, we should find all our extra font files, and hopefully in the right places. However, attempting to use them won’t work because we’ve not run updmap yet. Building some LaTeX documents may not error now, but we’ll get font substitutions happening, and most likely wind up with Computer Modern being used. Bleugh!

Running updmap

Back in the Debian world, there’s some dpkg-reconfigure incantation I used to run to do the necessary work. For some reason I’d hoped that Nix would just magically spot the extra .map files appearing in the output directories and automatically run updmap. Alas, it does not. So, we need to add some commands to be run right at the end of our mytexlive derivation being built. The way to do this is using overrideAttrs. This requires a little change to our myfonts.nix file:

  postCombineOverride = oldAttrs: {
    postBuild = oldAttrs.postBuild + ''
      updmap --sys --enable Map=Myriad.map --enable Map=Plantin.map
      updmap --sys
    '';
  };

  mytexlive = (pkgs.texlive.combine {
    inherit (pkgs.texlive)
      scheme-full;
    inherit latexFonts;
  }).overrideAttrs postCombineOverride;

We’ve added this postCombineOverride thing, and appended to the mytexlive section. What we’re doing here is that we’re replacing the postBuild commands from the derivation that results from the pkgs.texlive.combine function. Thankfully, we’re provided the existing value via oldAttrs, and so we just add onto the end of that. We enable the two extra map files we’ve provided, and then we regenerate the font databases by calling updmap --sys.

That should do it. Running nix-build -A mytexlive myfonts.nix again should get us a LaTeX which fully knows about our extra fonts.

Adding to home-manager

Just as we edited /etc/nixos/configuration.nix to make the OpenType and TrueType fonts available to the whole system, we can also add to home-manager to make our custom LaTeX system available to ourselves. So editing ~/.config/nixpkgs/home.nix:

{ config, pkgs, ... }:

let
  myfonts = import /home/matthew/.dotfiles/myfonts.nix { inherit pkgs; };

  homeManager = {
    ...
    fonts.fontconfig.enable = true;
    home.packages = [
      ...
      myfonts.mytexlive
      ...
    ];
  };
in homeManager

And then a final home-manager switch should add our customised LaTeX to our normal user profile.

Not a crazy amount of work, but a bit fiddly, and not easy to figure all this out. So I hope this is useful to someone!