Generate Custom PDFs with Pandoc, Panrun, and The Eisvogel LaTex Template

Originally posted on my blog here. Check it out for all the links out (this is my first post, so I am limited to 5 links)

Goals

I wanted to repeatably transform markdown into a stylish, custom pdf. I use markdown to take all of my note in thanks to my undying love for Obsidian. For my upcoming OSCP exam retake specifically, I wanted to be able to quickly and fluidly get my notes out of Obsidian and into a professional looking report that I could be proud of turning in.

My definition of done was this:

  • Markdown input produced PDF output
  • I could control enough of the style to make it my own
    • Code blocks should have syntax highlighting
    • I could use colors and assets from my ever unfinished portfolio site
    • Nice to have: Style is controlled within the markdown to affect the output PDF to make it easier to create a template
  • I can reuse the style easily in other reports
  • The process should be easily script-able

Tools

Style

I am by no means a designer, but I like to believe I am building a few brand assets with my portfolio site. The colors on the site are inspired heavily from the starship prompt, especially the magenta, which I modified ever so slightly and use everywhere, and the fonts I stumbled upon in fontjoy. I really like the techie look of the Overpass Mono headers, so I’d love to use those in my reports.

My intention was to create a template that I intend to use more than once, and I wanted the reports that utilize the template to be aesthetic. With that and the fact that with the Eisvogel LaTex Template allows you to set variables for page background and title page background, I went about creating those backgrounds in Gimp, which are the backgrounds on the pages in the linked document, and linked in the following section as titlepage-background: "TitlePageBackground.png" and page-background: "PageBackground.png".

Using Panrun and The Eisvogel Template

Panrun is a tool that allows you to set arguments for pandoc in the yaml frontmatter of the markdown document, so you don’t have to remember all of the arguments you must pass. My front matter ended up looking like this:

---
note_type: reference
writing_type: draft
title: Academy.htb
author: 5hade
titlepage: true
titlepage-rule-color: "ff0d8a"
titlepage-background: "TitlePageBackground.png"
titlepage-text-color: "eaecef"
page-background: "PageBackground.png"
colorlinks: "ff0d8a"
toc: true
toc-own-page: true
mainfont: DejaVuSerif
sansfont: Overpass Mono
linkcolor: magenta
urlcolor: magenta
listings-no-page-break: true
code-block-font-size: \scriptsize
output:
  pdf:
    pdf-engine: xelatex
    output: academy-htb.pdf
    from: markdown
    template: eisvogel
    listings: true
---

The top two yaml items are not for panrun, but are front matter I utilize in my Obsidian vault. The ones in the output block are used by pandoc, and all of the rest are used directly by the Eisvogel Template to style and format the PDF output.

Reusing The Template

Now that I’ve done this once, I can create a template to use when I want to output another report. I use the templater and my template looks like this:

---
note_type: <% tp.system.suggester(["thought", "media", "transient", "plan", "meeting", "presentation", "reference", "draft"], ["thought", "media", "transient", "plan", "meeting", "presentation", "reference", "draft"]) %>
writing_type: draft
title: <% tp.file.cursor(1) %>
author: <% tp.file.cursor(2) %>
titlepage: true
titlepage-rule-color: "ff0d8a"
titlepage-background: "<% tp.user.home_dir() %>/Pictures/pandoc/TitlePageBackground.png"
titlepage-text-color: "eaecef"
page-background: "<% tp.user.home_dir() %>/Pictures/pandoc/PageBackground.png"
colorlinks: "ff0d8a"
toc: true
toc-own-page: true
mainfont: DejaVuSerif
sansfont: Overpass Mono
linkcolor: magenta
urlcolor: magenta
listings-no-page-break: true
code-block-font-size: \scriptsize
output:
  pdf:
    pdf-engine: xelatex
    output: <%tp.file.title%>.pdf
    from: markdown
    template: eisvogel
    listings: true
---

Results!

Here are the results of making this document, as well as the write up for a retired HackTheBox machine I used to test the system out.


23 Likes

Impressive! Impressive :wave:

1 Like

Now this is of real value! Thank you so much for sharing!

(Finally someone who uses YAML to set Pandoc and LaTeX parameters in the right way!)

2 Likes

Great stuff!
What did you use to actually export the md note to Latex etc?

1 Like

Oh I don’t actually export the doc to latex ever. The latex template is something I found on github and provides options that I found were super useful. For me, with all the yaml in place, all I actually have to do is panrun <filename>.md, which will run pandoc with the options set in the yaml headers. In the template they are set as:

  pdf:
    pdf-engine: xelatex
    output: <%tp.file.title%>.pdf
    from: markdown
    template: eisvogel
    listings: true

which runs the same as pandoc --pdf-engine xelatex -o <filename>.md -f markdown --template eisvogel --listings where eisvogel is the template from github downloaded to my ~/.pandoc/templates directory

Do you have any ideas for alternatives to panrun?

  • Seems fiddly to have to install ruby just for this
  • I’ve installed it and it still seems like it isn’t running properly

I don’t see any alternatives in my package manager at all. Looking at the source code (panrun/panrun at master · mb21/panrun · GitHub) its only 185 lines of ruby, and could probably be easily ported into python or whatever makes sense for your use case.

I thought exactly the same and I’m going to try and create a parallel in python

1 Like

If you succeed, please do post the code here in this thread if you’re OK with that!

Plan would be to put something up on github. May take a little while, but will post back here once I have it.

3 Likes

FYI: I think you can even simplify it a bit, since Pandoc by now possesses an option for reading metadata from a yaml file with --defaults=FILE. So basically, you do not need panrun.

Nice workflow, nonetheless

Oh! That’s neat, I hadn’t seen that. With that, wouldn’t you lose the ability to use the substitutions that come with Templater? The way I read the section in the docs, it looks like this looks for a standalone yaml file instead of being able to read the yaml in the markdown frontmatter. Perhaps my understanding of that is incorrect. I think panrun serves the purpose of being able to change things on a per document basis, like if you wanted to change the filename or if you have different title pages for different reports for example.

1 Like

I’d love to contribute if you’re looking for one as well! Nothing against ruby, but I prefer all my tools pythonic!

1 Like

I think you are right. From what I can tell, there is an ongoing discussion among the pandoc devs whether to include all information into one file.

1 Like

Hello I saw your message so I created a python script based on panrun :smile:

I’m new with python so you can make it better