pandoc

2026-09-26

This post will be an overview of Pandoc. By the end of it we will have one PDF instead of a pile of documents.

What pandoc is

Pandoc is a tool to transform a document from one markup language to another. During many different processes we have to convert or interact with these documents, let's say, markdown documents to pdfs, etc.

Imagine you have some .tex or .docx documents and you want to unify them into one PDF! That's one of the things pandoc can do.

The thing is: there are many online tools that could do it, but many have serious compliance problems, and we have no control over what would happen to our documents :)

"A markup language is a text-encoding system which specifies the structure and formatting of a document and potentially the relationships among its parts. Markup can control the display of a document or enrich its content to facilitate automated processing." Source: wikipedia

Before Starting

While pandoc can be a bit spooky if you are not used to command line tools, I think it's worth the try.

If you're afraid of CLI and terminals, the carpentries have made the best tutorial I know to dip your fingers in the glory of Bash.

Starting out

Instead of presenting the tools we will learn the basics getting our hands dirty >:)

I've prepared some simple documents to this tutorial that you can grab from this repository

Introduction

Ahem Let's pretend the documents we just downloaded are the drafts we will submit to our PI. For that, we will need to unify our drafts and to generate a proper academic paper.

If you look inside the folder, you will see a map of our current chaos:

Installing

Ok, let's do it! First things first:

Installing the tool will depend on your operating system. Pick your poison:

windows

Linux

MacOS

Windows

Winget instructions

Choco Instructions

Install: winget install --source winget --exact --id JohnMacFarlane.Pandoc (or choco install pandoc)

Did it work? pandoc --version

For the PDF: you need a LaTeX engine too. MiKTeX is the one the official page points at:

choco install miktex

Or: swap the engine if you use typst: --pdf-engine=typst does the PDF with one small tool instead of LaTeX.

Linux

Install: your package manager has it (apt, dnf, pacman), with the warning from the official page: "check whether the pandoc version in your package manager is not outdated"

Did it work? pandoc --version

For the PDF: you need a LaTeX engine too. TeX Live, from the same package manager (apt install texlive).

Or: swap the engine if you use typst: --pdf-engine=typst does the PDF with one small tool instead of LaTeX.

macOS

If you already don't use it: HomeBrew

Install: brew install pandoc

Did it work? pandoc --version

For the PDF: you need a LaTeX engine too. brew install --cask mactex-no-gui is TeX Live whole (four gigabytes of disk, and it is what the journal's cover needs). If you prefer it small, brew install --cask basictex is the light one (around 400 MB), and then you install the missing pieces with tlmgr as they show up. They conflict with each other, so pick one.

Or: swap the engine if you use typst: --pdf-engine=typst does the PDF with one small tool instead of LaTeX.

The shape of the command

One basic command in pandoc would look like this:

pandoc draft.md -o draft.docx

or

pandoc draft.md -s -o draft.tex

pandoc has many flags, let's quickly explain the most important ones:

flag meaning
-o Output to a file
-s Standalone
-f From
-t to

With the last two flags, we could write a command to convert one of our files from markdown to latex, then produce an output in another format (let's say tex), like:

pandoc draft.md -f markdown -t latex -s -o draft.tex

It's just a more declarative way of doing it with the same result.

Question - how would we convert, using what we learned, the draft to a docx file? Answer

Output

-o is where the output goes: pandoc draft.docx -o draft.md. Without it, pandoc prints the result on the screen instead of writing a file.

Standalone

This is important! By default, Pandoc creates a fragment of a document, if we want the WHOLE thing, we will use this one :)

From

-f says what the input FORMAT is, for when the file extension does not tell it: pandoc -f markdown ....

To

-t says what the output FORMAT is, the other half of the pair above: -f markdown -t latex turns markdown into LaTeX.

Draft to docx:

pandoc draft.md -o draft.docx

And back to markdown:

pandoc draft.docx -t markdown -s -o converted_back_draft.md

Converting back a document

After Converting our draft to a docx file, what would happen if we converted back to markdown?

if we use cat converted_back_draft.md we will see that our references will come back looking like this: \[@lorem2025\]. instead of [@lorem2025]. so it's a thing to keep in mind.

Joining (what the PI asked for)

To join our pages, the YAML has to come from the first one: when pandoc gets more than one file, it reads the metadata from the first, and that is where the title, the authors and the abstract live.

And we have a new flag to learn: --citeproc, or -C for short. It is the citation rendering flag, and we will utilize it :)

Everything in, one file out
Front_page.md -> pandoc
draft.md -> pandoc
refs.bib -> pandoc
pandoc -> LaTeX engine
LaTeX engine -> paper.pdf

The command to Mix everything in a single PDF is:

pandoc Front_page.md draft.md -s --citeproc -o paper.pdf

In case you get a error similar to this:

'pdflatex' not found. Please select a different --pdf-engine or install 'pdflatex'

Means you don't have a Latex engine or didn't specify one (or typst if it's what you use).

if you already have one, you can declare using: --pdf-engine=your_engine_here.

And the engine is a tool of its own: that draft.tex we generated earlier compiles with no pandoc in the middle.

pdflatex draft.tex

or

pdflatex latexpaperfront.tex

It is the same engine pandoc was calling for us, which is why it wants packages. When one is missing it says which (the journal's own cover stops at File 'authblk.sty' not found on a fresh install or a simpler engine), and sudo tlmgr install <package> fixes it. It might ask for sudo tlmgr update --self in a point.

If you check the PDF now, you will realize the references did not have a place of their own. For that, we can do it in two ways:

1. in the YAML:

bibliography: refs.bib
reference-section-title: References

or 2. in the command:

pandoc Front_page.md draft.md -s --citeproc -M reference-section-title=References -o paper.pdf

The -M or --metadata flag serves to specify a metadata field in the command, it will override what is written in the file as well. For example, I can specify a new title for my paper with -M title=new_title.

What is YAML

The block between the --- at the top of a file. It is where a document carries its metadata: title, authors, date, abstract, keywords, and here the bibliography as well. Pandoc reads it as metadata instead of printing it on the page, and each output knows where to put it: LaTeX (and so the PDF) gets a real title page and an abstract for free, and the HTML gets a <title>.

--citeproc

The flag that resolves the citations. It reads the bibliography from the metadata (our refs.bib) and does two things: every [@key] in the text becomes a proper citation, and the reference list is appended at the end. -C is the short form of it.

This flag by default utilizes chicago style citations, There is a flag to change that: --csl=FILE. FILE can be any Citation Style Language File to format it, you can find it at: https://www.zotero.org/styles

Source

Stylizing

The journal's cover is a .tex, and we want pandoc to use it. That is what a template is: a LaTeX file with bodybody where your text goes.

The repository carries journal.tex: that same cover with bodybody in the place of the paper's text. Hand it over:

pandoc Front_page.md draft.md -s --citeproc -M reference-section-title=References --template=journal.tex -o paper.pdf

I know the command looks really big, but if you look at the flags, you will see many we have used already!

The flags, one by one

All the flags of the command
flag meaning
-s Standalone: the whole document, not a fragment.
--citeproc Resolve the citations and append the reference list (-C for short).
-M reference-section-title=References A metadata field set from the command line: the title of the reference list.
--template=journal.tex The shape of the document: the journal's cover, with bodybody where our text goes.
-o Where the output goes: paper.pdf.

And the two files at the start are the inputs, the first one bringing the metadata the paper uses: title, authors, abstract and the bibliography.

Just that, and every paper you write comes out in the shape the journal asked for. If you only want to nudge a detail, -H tweak.tex slips lines into pandoc's own header instead. Another thing is pandoc -D latex: it dumps pandoc's own template on the screen, and it ignores -o without complaining, so it never makes a file. It is also not the whole picture: the macros its writer emits, like \tightlist, are not in there.

One thing to expect: LaTeX will ask for things the cover never had, and they come in two kinds. A package it names (longtable, for a table) is one \usepackage away, or sudo tlmgr install <it> if your TeX is the small one. A macro it names (\tightlist, for a tight list; CSLReferences, for the bibliography) is the other kind: no package sells it, because it belongs to pandoc's own template. That is what a template has to carry, and it is why journal.tex has that block of definitions in it.

When the PI wants a docx

You delivered your pdf to your PI, but he decided to enter in vacations, your new PI doesn't work with .tex files, instead, he uses docx templates:

And when the output is not a PDF: docx, odt and pptx are not styled with a template but with a reference document, where you hand pandoc a file of the same kind and it copies the styles out of it (--reference-doc=reference.docx). The manual separates the two jobs: the reference doc adjusts the styles, the template interpolates the metadata. Handing a LaTeX template to a docx, by the way, breaks the file: pandoc drops the template inside it and it stops being a valid docx.

pandoc Front_page.md draft.md -s --citeproc -M reference-section-title=References --reference-doc=reference.docx -o paper.docx

Where to go next

The official stuff lives there, and it is where you go when this post runs out: the getting started guide and the MANUAL. There is also Pandoc for the people where it will use WASM to run pandoc in your browser (with a really nice GUI as well, in case you liked the idea of pandoc but not the terminal).

And when a paper stops being a paper: Quarto is a publishing system based on pandoc, where the same markdown becomes a book, a website or slides. It ships journal templates too, for when the cover has to look like Nature's.

← open in coffeeOS