Reference
Migrating an existing document pipeline, tool by tool
These are not the comparison pages. Those argue a choice: which tool, and why. These assume the choice is made and answer the next question, which is entirely mechanical.
The grouping below is the organising idea. A migration's shape is decided by the model the old tool used rather than by its name or its language. Leaving an engine that already took HTML is a CSS delta and a call change. Leaving a drawing API is a rewrite, because there were never any templates. Leaving a report designer means the layout lives in a file nobody can port. Those are three different projects, and two tools in the same language can be in different ones.
Every mapping on these pages points at options that exist: format, landscape, margin, scale, page_ranges, prefer_css_page_size, print_background, media, header_html, footer_html, pdf_a, password and callback_url, against the capability reference. Where an old tool had a feature with no counterpart here, the page says so instead of inventing one.
Already HTML and CSS
Your templates are assets and they mostly survive. The work is a CSS delta, because the old engine and a current one disagree about what they accept, and a call change, which is the easy half. These are the cheapest migrations and the ones where the surprises are subtle rather than structural.
- wkhtmltopdfCLI binary, wrapped from most languages
A command line tool built on a fork of an old browser engine, driven entirely by flags, where almost every flag you were passing has a different home on the other side.
- wicked_pdf and SnappyRuby on Rails, PHP and Laravel
Language wrappers that shell out to the wkhtmltopdf binary, where the framework integration is the thing you are actually replacing and the engine change comes along with it.
- dompdfPHP
A pure PHP renderer supporting a subset of CSS 2.1, where the migration's real content is everything your templates could not do and had to work around.
- mPDFPHP
A PHP renderer with its own HTML dialect, where the templates contain tags and attributes that exist only in mPDF and have to be translated rather than carried across.
- TCPDFPHP
A PHP library that is a drawing API with an HTML renderer bolted on, so most codebases using it are half template and half coordinates, and the two halves migrate differently.
- WeasyPrintPython
A Python renderer with genuinely good paged CSS support and no JavaScript engine, so the migration adds a capability you never had and changes very little else.
- PrinceCLI binary and language bindings
A commercial formatter with the most complete paged CSS implementation available, which makes this the one migration in the cluster where you should expect to give something up.
- PDFreactorJava, .NET, PHP and a server API
A commercial formatter with strong paged CSS support and its own extensions, where the migration is a feature audit followed by a fairly ordinary call change.
- Flying SaucerJava
A Java renderer that required well-formed XHTML and implemented CSS 2.1, where the migration relaxes both constraints and the templates keep obeying them anyway.
- PuppeteerNode
The same browser engine on the other side, so nothing about the document changes and everything about running it does.
- PlaywrightNode, Python, Java and .NET
The same shape of migration as Puppeteer, with a slightly different option vocabulary and the same absence of any CSS delta.
- Self-managed headless ChromeAny, usually behind a container
A browser you built infrastructure around, where the document does not change at all and what you are deleting is a category of operational work.
- PhantomJSCLI binary with a scripting API
A scriptable headless browser whose development ended years ago, so the CSS delta is the largest in the cluster and almost all of it is in your favour.
- html2canvas with jsPDFBrowser, client side
A pair of libraries that screenshot the DOM into a canvas and embed the image in a PDF, so the output was never text and the migration is the first time these documents become documents.
A declared document structure
The old code described a document rather than drawing one: a tree of blocks with styles, usually as JSON or a builder chain. That structure maps onto markup fairly directly, which makes this a translation rather than a rewrite, but the styling model is not CSS and the mapping is where the effort is.
- pdfmakeNode and browser
A library taking a JSON document definition, where the structure maps onto markup reasonably directly and the styling vocabulary does not map at all.
- @react-pdf/rendererReact, Node and browser
A React renderer with its own primitives and its own subset of styling, where the component structure survives and everything about how it is styled changes.
An imperative drawing API
There is nothing to port. The old code did not have a template, it had a sequence of drawing instructions with the layout expressed as arithmetic on coordinates. The document is written as HTML for the first time, and the value in the old code is the data layer underneath it, which survives unchanged.
- ReportLabPython
A Python library with a low-level canvas and a higher-level flowable system, where nothing in the layout is markup and the document has to be written as HTML for the first time.
- FPDF and fpdf2PHP and Python
A minimal drawing library built around Cell and MultiCell calls, where the layout is arithmetic and the arithmetic is the only specification of the document that exists.
- jsPDFBrowser and Node
A JavaScript drawing library whose documents are sequences of text and shape calls at coordinates, frequently run in the browser, so the migration moves the render to a server as well as changing its shape.
- PDFKitNode
A Node drawing library with a chainable API and a streaming output model, where the document is a sequence of calls and the stream is the part that changes shape.
- PrawnRuby
A Ruby drawing library organised around bounding boxes and a text cursor, with a real table extension, where the box structure is the closest thing to a layout you have.
- iTextJava and .NET
A mature PDF library with a document model and a low-level API, where the technical migration is ordinary and the reason for it is frequently the licence rather than the code.
- Apache PDFBoxJava
A Java library that operates close to the PDF format itself, where producing a document means writing content streams by hand and there is no layout engine at all.
A report designer or markup toolchain
The layout lives in a file somebody edited in a designer, or in a markup language with its own toolchain. None of it is portable, the design has to be rebuilt, and the honest question before starting is whether the reporting features around the layout are being replaced too.
- JasperReportsJava, with a desktop report designer
A reporting engine whose layouts are XML files built in a visual designer, organised into bands that repeat by rule, where nothing about the layout is portable and the band model has no equivalent.
- Crystal ReportsWindows, with a desktop designer
A reporting product whose layouts live in binary report files edited in a designer, with sections, formula fields and its own formula language, none of which are portable.
- SQL Server Reporting ServicesWindows and SQL Server
A reporting service whose layouts are RDL files built around a tablix, with the report server providing scheduling, subscriptions and delivery alongside the rendering.
- LibreOffice headlessCLI, usually in a container
An office suite run headless to convert documents to PDF, where the template is a word processor file and the conversion is a whole application invoked per document.
- docxtemplater and DOCX conversionNode and other languages, with a conversion step
A pipeline that fills placeholders in a Word template and converts the result to PDF, where the conversion step is what you are replacing and the Word file may still be a deliverable.
A formatter taking XSL-FO, an XML vocabulary for paged layout, usually produced from your data by an XSLT transform, where the transform is the template and both halves change.
- LaTeXA local toolchain, usually in a container
A typesetting system with its own language and a compilation step, where the migration is honest about losing typographic quality in exchange for everything else.
The safest way to do any of these
Put both paths behind one internal function that takes your data and returns bytes, and switch on an environment variable. You get a diff on real documents rather than on fixtures, you can move one call site at a time, and the way back is a variable rather than a deploy. Every page in this cluster assumes you are doing that, which is why the diffing section names what to compare rather than telling you to look at the output.