DEV Community

Cover image for Migrating from mpdf: an MIT PDF toolkit with no GPL friction
Denis Skripchenko
Denis Skripchenko

Posted on

Migrating from mpdf: an MIT PDF toolkit with no GPL friction

Every PHP shop that ships proprietary software eventually hits the same
wall: the de-facto standard HTML→PDF library, mpdf, is GPL-2.0-only. That
means bundling it into an OEM product, an on-premise installer, or any
closed-source codebase requires either shipping your product under GPL or
negotiating a commercial license. I hit that wall often enough that I
built a replacement.

php-pdf (github.com/dskripchenko/php-pdf)
is a pure-PHP, MIT-licensed PDF toolkit: HTML/CSS input, fluent builders,
TTF embedding with subsetting, barcodes, charts, AcroForm, PKCS#7
signing, PDF/A and PDF/X — plus reading and merging existing PDFs.

"Sounds too good" — so prove it

A two-week-old library claiming production crypto and PDF/A conformance
deserves skepticism. That's why every push to main runs an independent
proof chain in CI:

  • PDF/A references (1a/1b/2b/2u/3b) validated with veraPDF — the industry-standard validator, not our own code;
  • PDF/X structure checked byte-level plus a Ghostscript render pass;
  • signatures verified by the OpenSSL CLI against the exact /ByteRange bytes — and a tampered byte must break verification;
  • renders diffed against golden images with poppler.

Reports are public artifacts on every run:
conformance page.

The migration is mechanical

For the typical mpdf call site, swap one import:

// before
$mpdf = new \Mpdf\Mpdf(['format' => 'A4', 'margin_left' => 15]);
$mpdf->WriteHTML($html);
$mpdf->Output('invoice.pdf', 'F');

// after
$mpdf = new \Dskripchenko\PhpPdf\Compat\Mpdf(['format' => 'A4', 'margin_left' => 15]);
$mpdf->WriteHTML($html);
$mpdf->Output('invoice.pdf', 'F');
Enter fullscreen mode Exit fullscreen mode

The compat facade covers WriteHTML/AddPage/Output (all four
destinations), metadata setters and the common config keys — and it's
exercised by the test suite, so the examples in the
migration guide
are tested code, not aspiration. The guide also has the full native-API
mapping table and a find/replace cheat-sheet for Destination::*
constants.

What the facade deliberately does NOT do: mpdf-specific HTML extensions
(<pagebreak>, <barcode>), SetHeader shortcodes, font-config arrays.
Those map to the native API, which is better at all of them (real block
elements for headers/footers, a first-class Barcode element with 16
formats, engine-level font config).

And it's faster

Reproducible benchmark (isolated subprocesses, medians, pinned competitor
versions — composer bench from a clean clone re-runs everything):

Scenario php-pdf mpdf 8.3.1
HTML → PDF article (~5 pages) 12.0 ms 64.1 ms
100-page invoice 576 ms 2640 ms
Image grid (20 pages) 6.5 ms 36.1 ms

Full methodology and all four competitors:
BENCHMARKS.

Honest limitations

  • The HTML/CSS subset is narrower than mpdf's in places (@page rules map to a PageSetup object instead).
  • Nothing auto-embeds fonts: base-14 covers Latin; Cyrillic/Arabic/CJK need a TTF passed to the engine (it is subset automatically).
  • Public-key encryption (/PubSec) is intentionally unimplemented — we won't ship crypto we can't test against real fixtures.

Laravel users: there's a thin bridge —
dskripchenko/laravel-php-pdf
with a Pdf facade and a response()->pdf() macro.

If GPL friction has ever made you postpone a PDF feature, give it a try:
composer require dskripchenko/php-pdf. Issues and failing PDFs from
real-world producers are the most valuable thing you can send my way.

Top comments (0)