Converting markdown to PDF with a working table of contents

If you use {{TOC}} in your Markdown document, you’ll get a working table of content at the top of the preview in DT and also if you convert the MD to HTML in DT. “Working” means that clicking on an item in the TOC takes you to the corresponding part of the document.

However, converting the MD to PDF in DT produces a TOC with all the entries, but they are not working: Clicking on them does nothing. See below for some details.

One way to obtain a PDF with a working TOC is to use the MultiMarkdown 6 processor together with the Viviostyle CLI. To use the latter, you’ll need node.js and npm. Installing them is described in detail elsewhere, no need to repeat that here. How to install multimarkdown and Vivliostyle CLI is described on their respective websites I linked to above.

To convert an MD document with a TOC to a PDF

  • export the document from DT to a folder of your choice
  • open a terminal window and cd to this folder
  • first, run multimarkdown -o output.html -t html <>
  • then, run vivliostyle build output.html in the same folder
  • open the resulting file output.pdf and enjoy the lovely TOC

Instead of output.html you can, of course, use any file name you want. Both multimarkdown and vivliostyle support a host of options, so it’s worth to check their documentation.

The approach described above can also be encapsulated in a AppleScript or JavaScript script using do shell command or doShellComand so that you can run the conversion directly from DT.

Vivliostyle CLI relies on a headless Chrome browser to do its magic. That’s no drawback, since it’s installed automagically the first time you run Vivliostyle. And since Chrome’s support for CSS printing attributes is excellent, it is easy to fine-tune the appearance of the PDF.

TL;DR: Why the TOC doesn’t work in PDFs generated by DT

When DT converts a TOC from HTML to PDF, it creates a bunch of annotations of subtype link. However, these annotations have empty URL and destination elements. At least as far as I could find out, using my limited ObjC knowledge. Also, the contents property seems to be empty (which seems weird, but maybe I’m just doing something wrong).

Aren’t there simpler ways?

Theoretically, one could use Pandoc and wkthmltopdf instead of multimarkdown and Vivliostyle CLI. However, Pandoc’s MultiMarkdown parser seems to be a bit behind and is ignoring the {{TOC}} element completely. Which is not very helpful in this context.

Instead of using multimarkdown as described above, you could also convert the MD file to HTML in DT itself. However, in my experiments, that resulted in a web archive instead of a self-sufficient HTML file. And Vivliostyle does not work with these archives.

1 Like

An even easier workaround is to convert the Markdown document to HTML, to open this file in Safari and to print the page back to DEVONthink via its PDF service. Works at least on the latest macOS releases.