Dark Mode GPU PDFs with MuPDF

A Fast and Safe Viewer with Vim Mode

The MuPDF viewer it wicked fast on all fronts, supports dark mode (inversion), and has Vi bindings built in. However, it is maintained by Artifex, not hosted on GitHub or GitLab, and has ancient versions in most Debian-based distros.

Only the latest has a working -I inverted dark mode. It’s worth the time to compile it and compiling will be a good learning activity.

Building from Source

First get all the dependencies. You should be doing this from a device that has a graphics driver supporting OpenGL, which you probably do.

sudo apt install -y xorg-dev freeglut3-dev
Reading package lists... Done
  Building dependency tree
  Reading state information... Done
  The following NEW packages will be installed:
    freeglut3-dev xorg-dev
  0 upgraded, 2 newly installed, 0 to remove and 146 not upgraded.
  Need to get 128 kB of archives.
  After this operation, 812 kB of additional disk space will be used.
  Get:1 http://archive.ubuntu.com/ubuntu bionic/universe amd64 freeglut3-dev amd64 2.8.1-3 [124 kB]
  Get:2 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 xorg-dev all 1:7.7+19ubuntu7.1 [4,300 B]
  Fetched 128 kB in 1s (171 kB/s)
  Selecting previously unselected package freeglut3-dev:amd64.
  (Reading database ... 326158 files and directories currently installed.)
  Preparing to unpack .../freeglut3-dev_2.8.1-3_amd64.deb ...
  Unpacking freeglut3-dev:amd64 (2.8.1-3) ...
  Selecting previously unselected package xorg-dev.
  Preparing to unpack .../xorg-dev_1%3a7.7+19ubuntu7.1_all.deb ...
  Unpacking xorg-dev (1:7.7+19ubuntu7.1) ...
  Setting up xorg-dev (1:7.7+19ubuntu7.1) ...
  Setting up freeglut3-dev:amd64 (2.8.1-3) ...

Now you have to get the code.

If you haven’t already done so setup your local repos.

Then create a directory to contain the mupdf repo.

cd ~/repos
  mkdir git.ghostscript.com
  cd git.ghostscript.com

Now to a recursive clone to not only get the Git repo but also all the submodule repos within it.

git clone --recursive git://git.ghostscript.com/mupdf

This will have a lot of output.

Change into the new mupdf directory and then force the submodules to update as well (if they didn’t already).

cd mupdf
  git submodule update --init 

Again a lot more output.

Now you just need to build it. You can either make and install it for the whole computer and all users on it, or you can make a local copy. Just change the prefix to whatever you would like. Keep in mind that installing it for the system requires sudo, which is what this example shows.

The LFH calls for putting all installed user applications into /usr/local.

sudo make prefix=/usr/local install`

This will produce a massive amount of output and often pause and possibly look like it is failing even though it is not. Just be patient. You are compiling a rather large application written in C and C++ from scratch. Take a moment to be thankful for precompiled binary packages for most things.

When the compilation finishes you will see two new binaries in /usr/local: mupdf-gl and mupdf-xl. You can either use them this way or create an alias, functions, or small script to call them.

For example, to use the fast mupdf-gl with inversion on every time you type mupdf try adding the following alias to your Bash configuration.

alias mupdf='mupdf-gl -I'

Perhaps you want to consolidate all your PDFs into a common PDFDIR that keeps them organized.

First create the directory or project repo. Then export the PDFDIR in your Bash configuration something like this.

export PDFDIR=$HOME/repos/gitlab.com/rwxyou/pdfs

Now you can create a script like the following and put it in your dotfiles/scripts directory or someplace else in your PATH.

cd ~/repos/gitlab.com/rwxyou/dotfiles/scripts
  touch pdf

Here’s one example, tweak it as you like.


  if [ -z "$PDFDIR" ];then
    warn 'The `PDFDIR` env var not set.'
    exit 0

  list () {
    find "$PDFDIR" -name "*.pdf" -exec basename {} \;

  usage () {
    usageln 'list|usage|<pdf>'

  if [ -n "$COMP_LINE" ]; then
    prefix=$(echo "$COMP_LINE" | cut -d " " -f 2)
    list | grep ^$prefix
    exit 0

  case "$1" in
    list) list; exit ;;
    usage) usage; exit ;;

  # actually we have a pdf name, not a command
  # if gotten this far

  # always open using GL in dark (inverted) mode
  if [ -r "$pdfpath" ];then
    mupdf-gl -I "$pdfpath"  &>/dev/null &