Ask HN: Where are the good Markdown to PDF tools (that meet these requirements)?
I'm trying to convert a very large Markdown file (a couple hundred pages) to PDF.
It contains lots of code in code blocks and has a table of contents at the start with internal links to later pages.
I've tried lots of different Markdown-PDF converters like md2pdf and Pandoc, even trying converting it through LaTeX first, however none of them produce working internal PDF links, have effective syntax highlighting for HTML, CSS, JavaScript and Python, and wrap code to fit it on the page.
I have a very long regular expression (email validation of course) that doesn't fit on one line but no solutions I have found properly break the lines on page overflow.
What tools does everyone recommend?
57 comments
[ 3.3 ms ] story [ 146 ms ] threadConvert to HTML, then use Prince (https://www.princexml.com/) to style and convert to PDF.
Pretty nice to work with, if you can run JS. (The rest is just Puppeteer to print. Though I couldn't use their command line tool, because it force-injects paged.js, and it didn't play well with the Preact components for previewing I had made.)
Edit: I see it's $495 now. I don't think it was priced when I used it, but it's been 4-5 years.
I switched from using MD-->(Pandoc-->(latex))--> PDF to using MD-->(Pandoc-->(typst))--> PDF.
In a recent Talk Python to Me podcast [0], the Quarto [1] developers talked about how they are using Pandoc’s Lua interpreter [2] to perform transformations that aren’t part of vanilla pandoc in.md -o out.pdf.
0. https://talkpython.fm/episodes/show/493/quarto-open-source-t...
1. https://quarto.org/
2. https://pandoc.org/custom-writers.html
1. Download the app [01] 2. Create a new empty document 3. Insert a markdown section type 4. past your markdown code into the markdown section 5. click on "Preview & Export" 6. Configure your PDF
I'm the creator of MonsterWriter. For complex markdown it probably has some shortcomings but I would love to hear what is missing for your use case.
[01] https://www.monsterwriter.com/
Google\ Chrome --no-sandbox --headless --print-to-pdf-no-header --no-pdf-header-footer --enable-logging=stderr --log-level=2 --in-process-gpu --disable-gpu --print-to-pdf=resume.pdf "file://path/to/resume.html"
What worked well for me: Pandoc with a custom LaTeX template, and a decent amount of inline LaTeX to handle edge cases. We had a LaTeX theme to use from our publisher, but we also needed our own totally separate theme for the free version version of the book.
For one-off things like really long code lines, I found it best to manually figure out how to handle them. Sometimes there was a bit of TeX magic but, more often, I just rewrote or reorganized the code. I see the presentation and structure of code and math snippets as an integral part of how I'm communicating the underlying ideas, so manually changing things around to read better was fundamentally no different from going back and editing prose.
Unfortunately, this also means that the process was relatively hands-on. If you need something ≈completed automated, I expect Pandoc → LaTeX is going to fall a bit short. Edge cases need manual intervention, and it's easy for formatting errors to sneak in—the free version of our book has some formatting mistakes like code bleeding into the margin because I ran out of energy to fix all of them!
[1]: https://github.com/TikhonJelvis/RL-book
Keep in mind that you'll need to install custom fonts if you're using languages other than English.
https://github.com/syntax-tree/mdast-util-from-markdown
It might work better if you parse it into an intermediary Mdast format first, do whatever processing you need to implement "pages" (not a part of any Markdown dialect I'm familiar with?" but it shouldn't be hard to write a custom parser for that in Mdast), output that to HTML (via https://github.com/syntax-tree/mdast-util-to-hast) and THEN convert the HTML to PDF.
The AST tools basically give you structured JSON that's much easier to work with programmatically than raw Markdown. Then you can render that semantic JSON into HTML or other outputs.
I did a customized-MD pipeline which normalized to pandoc (extra features got encoded to pass through pandoc), obtained the pandoc JSON ast, and emitted html/latex/etc using Julia pattern matching. The code was small, and the yak shave and husbandry was worth escaping the struggle with sea of crufty candidate tools, each with assorted one-chosen-point in a high-dimensional design space, and missing features, misfeatures, gotchas, and mazes ("maybe if I combine this unmaintained plugin with that one and add a postprocess massage step over there and then maybe..." - blech).
My guess is that either toolchain could do the job... maybe just depends on personal preference whether someone prefers to pipe together command-line tools in a bash script, vs making use of the npm ecosystem (mdast is all in JS).
Maybe the popularity of JS & npm means there are available mdast plugins & third party packages that can help with whatever niche transformation you might need, and custom node rendering is just a lambda away. It's all in JS for a seamless experience, and there is no separate DSL to learn (just some basic helper functions).
That might be harder to do in Pandoc... (might need a custom Lua filter or another language like your Julia pattern matching?)
As for effectiveness... it probably just depends on the particular implementer :) I'd trust a grizzled old *NIX sysadmin type over your typical bootcamp JS programmer any day, but also... the JS ecosystem is pretty mature and powerful now, and Mdast is pretty amazing. At work we use it to build one of the most important parts of our app, and its power and flexibility never cease to amaze me.
So I guess two main observations. On build-vs-buy for backend features, given the breadth of possible "we want it like this, and not that", if one can easily play with ASTs, I was surprised by how quickly reinventing the wheel became a plausible call. Possibly skimming existing backend code for insight and templates, but mostly not using it (aka struggling to configure it to give you "this and not that"). The other observation, is once you have ast and don't care about existing backends, your choice of parser and backend language/ecosystem decouple. One might use `pandoc --to=json` and then JS generic-ast tooling to emit HTML.
For parsing, a glance suggests Mdast emphasizes CommonMark and Github-flavored dialects. Pandoc-flavored MD is a bit broader.[1] My fuzzy recollection is I chose a pandoc parse for that, and an expectation of robustness ("it's haskell, and popular"), despite the then less that wonderful docs. IIRC, the resulting asts were fine. For backend, I wanted simple and concise to minimize burden, thus pattern matching (IIRC, most node types ended up a line or two), and chose road-less-traveled Julia for off-topic reasons (was thinking of using Julia for a compiler backend).
Thanks for your thoughts on Mdast - I'm tempted to play with it.
[1] https://garrettgman.github.io/rmarkdown/authoring_pandoc_mar...
[1]: https://github.com/w4rh4wk/dogx
[2]: https://github.com/W4RH4WK/M.Sc.-Thesis/blob/master/output/t...
Sphinx and jupyter-book support MyST Markdown.
PDF Tables of Contents with links to headings or page numbers are possible with MyST and RestructuredText.
I believe it’s Mac only. I use it sometimes when I’m creating PDFs from my personal documentation to share more publicly, which I keep in Markdown and deploy on Gitlab Pages as a static site.
https://docs.python.org/3/library/re.html#re.X
I've worked on PAIP, Paradigms of Artificial Intelligence Programming, and I might be able to help you a bit. It's around 1k pages long. I used Pandoc to generate an epub file, and then Calibre to turn that into a PDF file. I just tried using Pandoc to generate the PDF file directly, and it/LaTeX choked on some Unicode characters.
For internal ebook links, there's a Lua script. You'll have to keep anchors unique across the book for this:
* good: "chapter1#section1_1" and "chapter2#section2_1"
* bad: a "chapter1#section1" and a "chapter2#section1"
WIP: https://github.com/norvig/paip-lisp/pull/195
For line wrapping of code, there's CSS. I first used it over on "Writing an Operating System in 1,000 Lines"; here's the PR: https://github.com/nuta/operating-system-in-1000-lines/pull/...