PHP Libraries for Converting HTML to PDF
Compare Dompdf, mPDF, tc-lib-pdf, Chromium and wkhtmltopdf, with runnable PHP examples and guidance for choosing the right renderer.
Short answer: choose Dompdf for straightforward documents where CSS 2.1-level layout is enough; choose mPDF for UTF-8-heavy, print-oriented documents with pagination, headers, footers, tables of contents, barcodes or RTL text; choose tc-lib-pdf when deterministic pure-PHP rendering, PDF/UA structure, signatures or conformance controls matter; and choose a Chromium-backed tool such as Browsershot or Gotenberg when reproducing modern website CSS is the priority. Treat wkhtmltopdf as a legacy compatibility choice because its QtWebKit engine predates much of CSS3 and its upstream project was archived in January 2023.
The architectural decision comes first: pure-PHP layout engines parse HTML and implement a defined subset of CSS themselves, while browser-backed libraries delegate rendering to Chromium, QtWebKit or a conversion service. Those approaches have different fidelity, operations, memory, accessibility and reproducibility trade-offs.
1. Decision table
| Option | Rendering model | Best fit | Watch-outs |
|---|---|---|---|
| Dompdf | Pure PHP; mostly CSS 2.1 | Invoices, letters and reports with conventional layout | Modern CSS support is limited; create a new instance per document |
| mPDF | Pure PHP, print-focused | UTF-8, RTL, page controls, headers/footers, barcodes and tables of contents | Validate complex web-app CSS and memory use on representative files |
| tc-lib-pdf | Pure PHP; defined HTML/CSS subset | PHP 8.2+, PDF/UA structure, signatures and conformance controls | Not a full browser; unsupported CSS must be redesigned |
| Browsershot 5.4 | Chromium through Node and Puppeteer | High modern-CSS fidelity | Install, patch and operate Node/Chromium; output can change after engine updates |
| Gotenberg PHP 2.25 | HTTP service running Chromium and LibreOffice | Teams that want browser rendering behind a separate service | Operate and monitor the service; network and queue failures become part of the path |
| Snappy / wkhtmltopdf | QtWebKit binary | Existing deployments whose output depends on its old behavior | Archived upstream and old CSS engine; validate every important layout |
These tools are documented by their projects: Dompdf describes a mostly CSS 2.1 PHP renderer; mPDF generates PDFs from UTF-8 HTML; tc-lib-pdf/TCPDF documentation describes direct rendering of a subset of HTML and CSS; and the PHP manual documents libwkhtmltox as an LGPLv3 QtWebKit renderer.
2. Dompdf: the simplest pure-PHP starting point
Dompdf is a mostly CSS 2.1 compliant HTML layout and rendering engine written in PHP. It handles external stylesheets, media and page rules, tables with row and column spans, and common raster image formats. It bundles the R&OS CPDF class, so no external PDF library is required. PDFLib is optional; the project says it can improve performance and reduce memory requirements.
Install
composer require dompdf/dompdf
Runnable PHP example
<?php
require __DIR__ . '/vendor/autoload.php';
use Dompdf\Dompdf;
use Dompdf\Options;
$options = new Options();
$options->set('isRemoteEnabled', true); // Only enable when you trust the URLs in the document.
$options->set('defaultFont', 'DejaVu Sans');
$dompdf = new Dompdf($options);
$html = '<!doctype html>
<html><head>
<meta charset="utf-8">
<style>
@page { margin: 18mm; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11pt; }
h1 { color: #17324d; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #bbb; padding: 6px; }
</style>
</head><body>
<h1>Invoice 1007</h1>
<p>Created: 2026-10-01</p>
<table><tr><th>Item</th><th>Amount</th></tr>
<tr><td>Implementation</td><td>€1,200</td></tr>
</table>
</body></html>';
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
file_put_contents(__DIR__ . '/invoice.pdf', $dompdf->output());
Do not reuse one Dompdf instance for multiple HTML documents. The project warns that parsing and rendering artifacts can persist; construct a fresh instance for each document.
3. mPDF: print features and international text
mPDF is designed for UTF-8 encoded HTML. Its print-oriented feature set includes color handling, pre-print, barcodes, headers and footers, page numbering, tables of contents and right-to-left text.
Install and generate a document
composer require mpdf/mpdf
<?php
require __DIR__ . '/vendor/autoload.php';
use Mpdf\Mpdf;
$mpdf = new Mpdf([
'format' => 'A4',
'margin_left' => 18,
'margin_right' => 18,
'margin_top' => 22,
'margin_bottom' => 20,
'tempDir' => __DIR__ . '/tmp/mpdf',
]);
$mpdf->SetTitle('Quarterly report');
$mpdf->SetHTMLHeader('<div style="font-size:9pt">Acme · Quarterly report</div>');
$mpdf->SetHTMLFooter('<div style="font-size:9pt">Page {PAGENO} of {nbpg}</div>');
$mpdf->WriteHTML('<h1>Quarterly report</h1><p>UTF-8: Café, Ελληνικά, العربية</p>');
$mpdf->Output(__DIR__ . '/report.pdf', 'F');
Set a writable temporary directory in production and install fonts that cover every script you output. Test RTL paragraphs, mixed-direction numbers and long unbreakable strings separately from Latin text.
4. tc-lib-pdf: structure, signatures and conformance
tc-lib-pdf is the current generation of TCPDF, split into focused Composer packages and documented for PHP 8.2 and later. Its HTML renderer supports a defined subset of HTML and CSS directly, without a browser engine. The project documents PDF/UA structure generation, tagged text, figure alternative text, form-field descriptions, signatures and other conformance controls.
Install and render a basic document
composer require tecnickcom/tc-lib-pdf
<?php
require __DIR__ . '/vendor/autoload.php';
use Com\Tecnick\Pdf\Tcpdf;
$pdf = new Tcpdf();
$pdf->setCreator('Example application');
$pdf->setTitle('Accessible report');
$pdf->setAuthor('Example application');
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->AddPage();
$pdf->writeHTML(
'<h1>Accessible report</h1>' .
'<p>Use the renderer\'s documented tagging and conformance controls for your target profile.</p>'
);
$pdf->Output(__DIR__ . '/accessible-report.pdf', 'F');
Confirm the exact package namespace and conformance APIs against the version you install. Keep your HTML inside the documented subset; browser-only layout features will not be interpreted as they are in Chromium.
5. Browser-backed PHP options for modern CSS
When the input is an existing responsive web page using Grid, Flexbox, web fonts, JavaScript layout or complex print styles, a Chromium-backed path generally gives the closest visual result. Browsershot spawns Chromium through Node and Puppeteer. Gotenberg exposes Chromium and LibreOffice through an HTTP service. Both add an engine or service to install, patch and keep running. Engine updates can change output, so pin versions and compare representative PDFs before upgrading.
Browsershot example
composer require spatie/browsershot
npm install puppeteer
<?php
require __DIR__ . '/vendor/autoload.php';
use Spatie\Browsershot\Browsershot;
Browsershot::url('https://example.com/invoice/1007')
->setNodeBinary('/usr/bin/node')
->setNpmBinary('/usr/bin/npm')
->showBackground()
->paperSize(210, 297) // millimetres
->margins(12, 12, 12, 12)
->save('/tmp/invoice.pdf');
Run Chromium in a restricted service account, supply explicit executable paths in containers, and decide how fonts, outbound requests, JavaScript and authentication are handled before exposing this to user-supplied URLs.
Gotenberg request from PHP
<?php
$ch = curl_init('http://gotenberg:3000/forms/chromium/convert/url');
$post = ['url' => 'https://example.com/invoice/1007'];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $post,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 90,
]);
$pdf = curl_exec($ch);
if ($pdf === false) {
throw new RuntimeException(curl_error($ch));
}
curl_close($ch);
file_put_contents(__DIR__ . '/invoice.pdf', $pdf);
6. wkhtmltopdf and libwkhtmltox: when legacy compatibility wins
wkhtmltopdf uses QtWebKit. Its upstream project was archived in January 2023, and QtWebKit predates much of CSS3. Keep it when an existing deployment depends on its known pagination or rendering quirks, but treat it as a compatibility path for new work only after testing modern CSS, fonts, SVG, JavaScript and security behavior.
7. A repeatable selection process
- Inventory the input. Record CSS features, JavaScript dependencies, external fonts, images, SVG, tables, RTL scripts and page-break rules.
- Choose the rendering class. Select pure PHP for controlled templates and minimal process operations; select Chromium when browser fidelity is the primary requirement.
- Check document requirements. PDF/UA tagging, signatures, encryption, page boxes and conformance controls may favor tc-lib-pdf or a second PDF-processing stage.
- Build a fixture set. Include long tables, overflowing cells, missing images, web fonts, Unicode, RTL, charts, repeated headers and forced page breaks.
- Pin and review. Lock Composer, Node, browser and OS packages. Compare output after upgrades because rendering engines evolve.
8. Pagination, fonts and assets
- Use print styles and explicit page margins. Keep headings with their following content where your renderer supports the rule.
- Prefer locally installed fonts for reproducibility. Verify licensing and fallback coverage for every language.
- Embed or allow-list images and stylesheets. Remote assets introduce DNS, TLS, authentication and timeout failures.
- Break very large tables into logical sections. Browser and pure-PHP engines can consume substantial memory while laying out one enormous table.
- Test links, bookmarks, metadata and accessibility separately from visual screenshots. Similar-looking pages can have very different PDF structure.
9. Security checklist
- Never pass untrusted HTML into a renderer without sanitizing it.
- Disable remote URLs unless required, then use an allow-list and network egress controls.
- Run Chromium, wkhtmltopdf and conversion workers as an unprivileged user with filesystem and process limits.
- Set CPU, memory, wall-clock and output-size limits. Reject unexpected redirects and private-network destinations.
- Keep secrets out of HTML, command-line arguments and generated PDFs. Scrub temporary directories after completion.
10. Performance, reliability and cost
There is no universal fastest library. Rendering time and memory depend on HTML size, CSS complexity, fonts, images, JavaScript, PHP configuration and the runtime environment. Pure-PHP tools avoid a browser process and are often simpler to deploy, while Chromium adds startup and service overhead in exchange for modern CSS behavior. Measure your own fixture set rather than relying on a generic benchmark.
For throughput, reuse workers carefully, queue expensive browser jobs, cache immutable assets and record renderer versions with each output. For reliability, use bounded retries only for transient network or service errors, make jobs idempotent, and retain structured logs containing document ID, renderer, duration, exit status and output size.
License terms differ: the comparison identifies Dompdf, Browsershot, Snappy and Gotenberg PHP as MIT-licensed projects, while libwkhtmltox is documented as LGPLv3. Review the license and any bundled font or binary terms before distribution.
11. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| CSS layout collapses | Renderer does not implement the CSS feature | Reduce the template to the documented subset or move to Chromium |
| Images or styles are missing | Remote access disabled, bad URL, TLS or authentication failure | Use absolute URLs, allow-list hosts, embed assets or inspect network logs |
| Blank PDF | HTML parse error, fatal script error or premature process exit | Validate HTML, enable structured error logging and save the rendered input |
| Out-of-memory failure | Large tables, images, fonts or an oversized DOM | Resize assets, split documents, raise limits deliberately and profile the fixture |
| Wrong glyphs or tofu boxes | Missing font coverage or incorrect encoding | Use UTF-8, install a covering font and test fallback for each script |
| Headers repeat incorrectly | Unsupported page-break or table-header behavior | Use the library’s print APIs or switch renderer for that template |
| Chromium job times out | Slow JavaScript, blocked resource or unavailable browser process | Set a bounded timeout, wait on a specific readiness signal and inspect browser logs |
| wkhtmltopdf differs from the website | QtWebKit lacks modern CSS behavior | Keep a compatibility fixture or migrate to Chromium |
| PDF looks right but fails accessibility checks | Visual fidelity does not guarantee tagged structure | Use documented PDF/UA controls or a conformance-focused renderer such as tc-lib-pdf |
12. Or skip the browser setup
If your input is a public webpage and you need a clean capture rather than a PHP rendering pipeline, ScreenshotNeo provides a single API request. It also supports PDF capture, while the example below shows the standard screenshot endpoint.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for PDF options and the rest of the request parameters. Cookie banners, newsletter popups and chat widgets are removed before the shot; bot checks, blank pages and failed loads are never billed. An MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account to try it.
13. FAQ
Can I convert HTML to PDF without installing a browser?
Yes. Dompdf, mPDF and tc-lib-pdf are pure-PHP options. They avoid browser operations but support defined subsets of HTML and CSS.
Which library supports the most modern CSS?
A Chromium-backed tool generally provides the closest browser fidelity. Budget for Node/Chromium installation, patching and version pinning.
Is mPDF or Dompdf better for invoices?
Both can work. Pick Dompdf for simpler CSS and minimal dependencies; pick mPDF when UTF-8, RTL, page controls, headers/footers or barcodes drive the requirements.
When should I choose tc-lib-pdf?
Choose it when pure-PHP determinism, PDF/UA structure, signatures or detailed conformance controls outweigh full browser CSS fidelity.
Should a new project use wkhtmltopdf?
Usually only when maintaining an existing QtWebKit deployment or deliberately preserving its output. Validate modern CSS carefully because upstream is archived and the engine is old.
Why did a library work locally but fail in production?
Common differences include missing fonts, disabled remote access, filesystem permissions, PHP extensions, browser binaries, sandbox policy and network egress. Capture these dependencies in your deployment image and fixture tests.
