Single-source publishing with Pandoc and Make
For a while now, I’ve been looking for a way to apply single-source publishing techniquesFor more information on single-source publishing, check its page on Wikipedia and Adam Hyde’s article series.
to my PhD dissertation. By this, I mean writing my research in a single file (or collection of files) and exporting it in multiple formats—mainly HTML and PDF. This is trickier than it sounds: I have high expectations for the quality of these outputs, set by centuries of scholarly tradition, and this is difficult to do simultaneously across formats from a single source. I also favor free software and plain text, which narrows the pool of potential solutions.
Pandoc is at the heart of my workflow. It’s a phenomenal tool. I appreciate it especially because it allows me to separate writing from design—typography, layout, graphic design, etc. All the tinkering and the fine-tuning I like to do in CSS or LaTeX doesn’t interfere with the writing; when I need to focus on the latter, everything else fades. Thanks to this, I’ve been able to progress with my manuscript and work on its editorial design without these two tasks cannibalizing eachother.
Over the years, I found some great examples of automated processes based on Pandoc. I’ve ended up using two specifically: Ivan Stojic’s Pandoc-SSG, to generate my website, and Abrüpt’s GabaritGabarit means template in French.
, to generate my dissertation in PDF. Both are based on Make, a scripting tool which in these cases is used to organize a complex set of Pandoc commands. Of course, I wanted both solutions in just one tool.
I wasted quite a bit of time trying to merge Pandoc-SSG with Abrüpt’s Gabarit, but never succeeded. Recently, I decided to give single-source publishing another go but approaching the problem differently, this time writing my own Makefile from scratch. This turned out to be a much more sensible approach. Learning the basics of Make didn’t take long thanks to this excellent tutorial: Makefile tutorial by example. And I finally managed to devise a system that works for me, which I share with you today: Pandoc-SSP, a template for single-source publishing with Pandoc and Make.
I’ve called it a template but the term carries expectations I have to defuse. It’s a directory containing a Makefile with Pandoc commands, and a few other files and subdirectories. But I deliberately left the Pandoc options blank, and except for the Makefile, all the files are empty. So it’s not a functional template that you can test as is (like Abrüpt’s Gabarit for example). It’s more like a drawing on a blackboard that you have to re-draw yourself. If you’re a researcher looking to turn your scientific writing into HTML and PDF from a single source, this template provides directions to do so—but only directions.
Another disclaimer: this is a small tool, for basic needs. You may be interested in a more advanced system based on Pandoc, such as Manubot or Quarto. But if (like me) you usually try out simple tools first, my template might interest you.
A final warning: if you do not know Make, I encourage you to read a tutorial before using this template. It did me a world of good. Again, this one is brilliant: Makefile tutorial by example. When I had questions it didn’t directly answer, it provided a link to the documentation where I immediately found solutions. I advise you to pay special attention to these three cryptic signs:
$^. Look closely in my Makefile and you’ll find they do most of the heavy lifting where Pandoc is involved.
How it works
The idea is simple: put everything in a folder and use Make to automate a series of file conversions with Pandoc. Some files are used in each conversion process (text, references, bibliographic style); others are format-specific (template, metadata).
The Makefile is where the magic happens. There are a few lines you may want to leave as is, the ones that enable the recursive copy of static content (a trick I borrowed from StackOverflow). Everything else can be adapted (I did not bother to use variables for customizable things, like paths… feel free to do your own thing).
Again, the template does not work as a self-demonstrating example. Apart from the Makefile (where the process is defined), all the files are dummy files. If you download the repository and try running
make, nothing will happen. Instead, you need to replace the files with your own.
As it is written, the Makefile uses the following folders:
text: Markdown files, which you organize any way you want (subdirectories are helpful if you want different Pandoc commands for different groups of files)
output: output folder
static: static folder, for anything that needs to be copied as is in the output (e.g., for the HTML version: CSS, fonts, images…)
The root contains:
metadata.yml: metadata which Pandoc can use to fill the templates
- templates, such as
references.bib: bibliographic data (could be CSL JSON or any other format supported by Pandoc)
style.csl: bibliographic style
index.md: this becomes the
index.htmlof the HTML version
You need to drop in your own files and add Pandoc options yourself. Pandoc has amazing documentation: check Pandoc’s User Guide and the Pandoc templates repository.
Here is an example diagram of the conversion process. Your mileage may vary. For instance, you may want to use
metadata.yml in both conversion processes.
Click here to download a one-page PDF cheatsheet with the diagram, file tree and Makefile.
How to use it
Download the repository and replace the files with your own. Then, open a terminal and move to the directory. Run either of the following commands to export both HTML and PDF:
make all. Run the following command to export HTML:
make html. Run the following command to export PDF:
It may not seem much, especially when compared to the awe-inspiring stuff that exists now, like Manubot and Quarto. But I like it. I like the fact that it’s so simple. I like that you can drop in your template, whether it’s Pandoc’s default HTML template or some crazy customized LaTeX file. And I like the idea of a template that’s not a fully functional thing but something you need to adapt in order to see it work.
I’m not committing to any kind of support on this project. So if you see room for improvement, fork the repository and have fun with it!