Comparison

urltodoc vs wkhtmltopdf

wkhtmltopdf shipped a generation of invoices and deserves the credit. But it is archived, and the browser engine inside it stopped following the web around 2016 — which is exactly when your CSS started using flexbox and grid.

The engine is frozen

Old WebKit means no grid, patchy flexbox, no modern colour functions, and CSS you have to write twice — once for browsers and once for the PDF.

The project is archived

No upstream fixes and no security patches for a binary that fetches remote URLs on your infrastructure. That question comes up in every audit.

JavaScript is a guess

A fixed --javascript-delay is either too short for the chart or wasted on every other document.

Concern wkhtmltopdf urltodoc
Rendering engine wkhtmltopdfPatched WebKit from around 2016. No CSS grid, partial flexbox. urltodocCurrent Chromium, version-pinned per project.
Maintenance wkhtmltopdfArchived upstream. No fixes, no security patches. urltodocActively maintained, with a public status page.
JavaScript wkhtmltopdfA fixed delay, hopefully long enough. urltodocWait for a selector, an event, or network idle.
Web fonts wkhtmltopdfUnreliable @font-face; system fonts only in most images. urltodoc@font-face plus 1,100 bundled families and your own uploads.
Page breaks wkhtmltopdfIts own heuristics; break-inside is mostly ignored. urltodocNative print CSS. break-inside and @page do what they say.
Headers & footers wkhtmltopdfFlags with bracket placeholders, styled separately. urltodocHTML fragments with {page}, {total} and {date}.
Merging documents wkhtmltopdfA second tool, and font subsets that sometimes break. urltodocmerge with continuous pagination and a table of contents.
Operations wkhtmltopdfA binary in your image, run as a subprocess, sized by you. urltodocAn HTTP call. Elastic capacity, async and batch included.

Tables back to grid

Layout tables and float hacks can go. A current engine prints the same CSS your app already uses.

Real web fonts

@font-face works, and we wait for document.fonts.ready before printing.

Native page control

break-inside and @page behave, so the total block stops splitting.

No more delay tuning

Replace --javascript-delay 5000 with a selector, and get seconds back per render.

Documents you archived with the old engine will not render byte-identically — the engine is different. Pin a version now so the new ones stay stable.

# before wkhtmltopdf \ --page-size A4 \ --margin-top 18mm --margin-bottom 18mm \ --javascript-delay 5000 \ --enable-local-file-access \ --footer-center "[page] / [topage]" \ https://acme.com/invoices/42 invoice.pdf # after curl -X POST https://urltodoc.com/api/render \ -H "Authorization: Bearer $URLTODOC_KEY" \ -d '{ "url": "https://acme.com/invoices/42", "format": "A4", "margin": { "top": "18mm", "bottom": "18mm" }, "wait_for": "#invoice-total", "footer_html": "<div>{page} / {total}</div>" }' --output invoice.pdf

Send us your worst template.

The one with the layout tables and the five-second delay. We will tell you what to delete.