Ubikium

Home About Portfolio

Maintainable Beamer-Markdown with Nix

Posted on 2019-11-30 by ubikium

Update

Please refer to the new post where nix flake is used to make the process easier. This post is only preserved for references.

Introduction

Nix is cool, Markdown is easy, and LaTeX-Beamer-Pandoc is powerful. How to combine the three together? Here I will introduce some configuration tricks I learned during the process. The main purpose of the post is to automate as much as possible and actually make the configuration maintainable.

Most of the contents of the post is from websites, with a little adaptation. The Nix part comes from kimagure’s wonderful post Easy Markdown to Beamer with Pandoc and Nix. The separation of configuration comes from Boilerplating Pandoc for Academic Writing. Plus some good documentations from TeXLive packages.

Installation

For this to work, you need to install Pandoc and some TeXLive packages. My configuration is as following:

let
  pkgs = import <nixpkgs> {};

  texlive = pkgs.texlive.combine {
    inherit (pkgs.texlive)
    scheme-full;
  };
in {
  slides = pkgs.stdenv.mkDerivation {
    name = "slides";
    src = ./.;

    buildInputs = [
      texlive
      pkgs.pandoc
      pkgs.watchexec
    ];
  };
}

Notice that I’ve changed TeXLive schemes into the full one, meaning that you will get every package on TeXLive. This solves a lot of difficult dependency problems, and unless the disk storage is a problem, the full scheme is recommended. Of course, you can replace that with a basic one and add the packages you need. See the Nix manual’s TeXLive section.

Save this as a default.nix file, which will be invoked whenever nix-shell runs. You have to copy-paste this file to the directory where your source markdown file lives. I’ll introduce the automation technique later.

Makefile

Following the same Makefile from the original post,

default:
    nix-shell --run 'make slides'
slides:
    pandoc -t beamer slides.md -o slides.pdf
watch:
    nix-shell --run 'watchexec -e md make'
repl:
    nix repl '<nixpkgs>'

When you invoke make command, the pandoc command is run in the nix-shell, which uses default.nix to install the dependencies. As is the default.nix file, you’ll have to put the Makefile wherever you write slides, and the slide source file’s name needs to be slides.md.

Boilerplate the slide

You can try it now with the following slides.md file,

---
title: Maintainable Markdown-Beamer
subtitle: a survey
author: Ubikium
date: 2019-11-29
---

# Introduction

## Motivation

Why *Markdown*?

- It's easy.
- It's simple.

Why `Beamer`?

- It's nice.
> - It has incremental bullets.

# Tests

## Mathematical symbols

Suppose $R$ is the radius of a sephere.

Then the volume can be calculated with the following formula:

$$V = \frac{4\pi}{3} R^3$$

## Code

```hs
data Maybe a = Just a | Nothing
```

## Inline LaTeX

\begin{center}
  \emph{Hello, World!}
\end{center}

Copy the default.nix and Makefile to the directory where you put the markdown file, and run make. You should see the slides.pdf file in the same folder. The first time will take a while to actually install the dependencies, but consecutive calls are much faster. That’s the magic of Nix.

You can add a lot of things in the metadata part of the markdown file, including the usual filetype independent author, keywords, and abstract. Also you can add some LaTeX command to add before the header, like installing a font and using some packages.

According to the Boilerplate post, it’s better to separate the useful metadata (e.g. title, author, date, abstract) and the formatting metadata (e.g. fontsize, theme). Leave the former in the header of the markdown source file, and put the latter into a separate file. Let’s call it default.yaml. Here is my setting:

---
toc: true
toc-title: Table of Contents
fontsize: 12pt
---

Copy it to the source directory and the pandoc command should be pandoc -t beamer default.yaml slides.md -o slides.pdf.

For the LaTeX commands to be added before the document, why not seperate them as well? Because they tend to be many and they’re more or less the same each time.

My settings for the header is as following:

\usepackage{amsmath, amsfonts, amssymb}
\usetheme{metropolis}
\usepackage{FiraSans}
\usepackage{mathptmx}
\def\mathfamilydefault{ptm}

% Toggle text font between Sans Serif & Serif (math mode font unchanged)
%   \renewcommand{\familydefault}{}           # sans serif
%   \renewcommand{\familydefault}{\sfdefault} # serif
\renewcommand{\familydefault}{\sfdefault}

% Dummy command to enable markdown inside raw latex
% example:
%   \tex{
%     \begin{center}
%   }
%
%   Markdown *here*.
%
%   \tex{
%     \end{center}
%   }
\newcommand\tex[1]{#1}

% useful symbols
\newcommand{\heart}{\ensuremath\heartsuit}
\newcommand{\coheart}{\rotatebox[origin=c]{180}{\heart}}

\newcommand{\dialabel[1]}{\ensuremath\langle#1\rangle}
\newcommand{\boxlabel[1]}{\ensuremath[#1]}

\definecolor{DeepBlue}{RGB}{0, 0, 78}
\setbeamercolor{title}{fg=DeepBlue}
\setbeamercolor{frametitle}{bg=DeepBlue}
\setbeamercolor{structure}{bg=DeepBlue}

Note that if you need academic writing and want to use mathptmx as your math mode font, the package must comes after the theme command.

You can keep the text font unchanged by setting \renewcommand{\familydefault}{\sfdefault}. The other \tex{} command is a workaround to write markdown inside inline LaTeX. I found that useful. See this issue.

Save it as header.tex, and this concludes the final form of the pandoc command. You can edit the Makefile to the following:

default:
	nix-shell --run 'make slides'
slides:
	pandoc -t beamer -H header.tex default.yaml slides.md -o slides.pdf
watch:
	nix-shell --run 'watchexec -e md make'
repl:
	nix repl '<nixpkgs>'``

Automating copying

For now, we have used files to boilerplate out most of the settings. But that’s a lot to copy each time, you need to have:

  1. default.nix
  2. default.yaml
  3. header.tex
  4. Makefile
  5. slides.md

in your slides directory. Why not automate with some bash scripts.

Choose a directory that’s on your $PATH environment variable. For example, ~/.local/bin. Create a new script as following:

#!/run/current-system/sw/bin/bash

# Check if:
#   default.nix
#   default.yaml
#   header.tex
#   Makefile
#   slides.md
# exists
# if not, copy from templates

BEAMERTEMP="/path/to/your/templates/beamer"

SLIDES="slides.md"

TODAY=$(date +%F)

check_and_copy () {
    if test -f $1
    then
        echo "Using current $1"
    else
        echo "Creating $1"
        cp "$BEAMERTEMP/$1" $1

        # set slides date to today
        if [ $1 == $SLIDES ]
        then
            sed -i "s/^date:.*$/date: $TODAY/" $SLIDES
        fi
    fi
}

check_and_copy "default.nix"
check_and_copy "default.yaml"
check_and_copy "header.tex"
check_and_copy "Makefile"
check_and_copy $SLIDES

This script will check if each file exists, if it already exists, just use them, otherwise, copy from the templates.

Don’t forget to change the path to your templates. It also changes the date: into the creating day.

Also note that the shebang at the top of the script is for NixOS. If you’re not on NixOS, then it typically should be #!/bin/bash. If you are not sure, just use which bash.

Save it as beamer-init, chmod u+x beamer-init. By the way, you should also set the permission for other users.

If the directory is not already in your $PATH variable, add it to your .bashrc and source it.

Conclusion

For a new slide show, just beamer-init, then add the content, change some options maybe, and finally make. That’s easy!

Hope this will help you, my friend.

Files

All files mentioned are available in the following URLs:

  1. default.nix
  2. default.yaml
  3. header.tex
  4. Makefile
  5. slides.md
  6. beamer-init

Feed: Atom/RSS Site proudly generated by Hakyll.
Fonts: Serif - Merriweather, Monospace - FiraCode
Theme adpated from The Professional designed by Dr. Kat.
Original theme showcased in the Hakyll-CSSGarden.

All contents are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.