Migrate from mPDF to NextPDF
At a glance
Section titled “At a glance”This guide helps you move an mPDF-based codebase to
NextPDF core. In mPDF, the main content call is WriteHTML(), which maps
directly to Document::writeHtml(). Most of the work is the constructor
config-array map (mPDF configures everything through one associative array passed
to new Mpdf([...])) and the font-handling delta. NextPDF and mPDF are
independent engines, so a migrated document is compatible with the mPDF
output, not byte-identical to it. This guide covers the verb map, the
config-array map, the font delta, the Cascading Style Sheets (CSS) support delta,
the behavioral differences, and a safe sequence.
The Cascading Style Sheets (CSS) support matrix defines which Hypertext Markup Language (HTML) and CSS features are Verified. This guide describes behavior; it does not assert visual equivalence with mPDF.
Install
Section titled “Install”composer require nextpdf/core:^3Keep mpdf/mpdf installed while you migrate. Remove it after the final cutover
(see safe migration sequence).
Conceptual overview
Section titled “Conceptual overview”mPDF’s Mpdf object combines configuration and rendering behind one interface.
A constructor array configures it, and WriteHTML() plus the paging verbs
(AddPage, SetHTMLHeader, SetHTMLFooter) drive it. NextPDF separates
configuration into the immutable NextPDF\Core\Config value object and writes
content with Document::writeHtml(). There is no constructor “mode” string.
NextPDF parses the HTML you pass, then emits the document with save(),
output(), or getPdfData().
Fonts: mPDF ships with a font directory, a fontdata map, and a “core fonts”
fallback set. NextPDF resolves fonts from one fonts directory plus CSS
font-family matching, and always subsets embedded fonts (International
Organization for Standardization (ISO) 32000-2 §9,
iso32000_2_sec9#x1.x45.p7). Font matching and fallback are engine-specific
(CSS Fonts 4 §5.5, css_fonts_4#x1.x5.x5.x1.p13), so glyph substitution can
differ. This is the main visible delta, covered in the font-handling
delta.
API surface
Section titled “API surface”NextPDF’s HTML API is documented in the Html module
reference. The core entry points are Document::createStandalone(),
Document::writeHtml(string $html): static,
Document::writeHtmlCell(...), Document::addPage(),
Document::output(?string, OutputDestination),
Document::save(string $path): void, Document::getPdfData(): string, and the
NextPDF\Core\Config value object.
API verb mapping
Section titled “API verb mapping”The mPDF public method names below are confirmed against the upstream public
repository (mpdf/mpdf, development); see the in-repo
_source-sidecar-upstream-api.md provenance sidecar. No upstream documentation
text is reproduced.
| mPDF | NextPDF | Notes |
|---|---|---|
new Mpdf([...]) | Document::createStandalone($config) | The mPDF config array maps to NextPDF\Core\Config; see config map. Use DocumentFactory for long-lived workers. |
$mpdf->WriteHTML($html) | $doc->writeHtml($html) | Direct map. mPDF’s second $mode argument (full document vs. CSS-only vs. element) has no NextPDF analogue; pass complete HTML. |
$mpdf->WriteFixedPosHTML(...) | $doc->writeHtmlCell(...) | Positioned and sized HTML region; map x/y/width/height arguments. |
$mpdf->AddPage(...) | $doc->addPage() | NextPDF does not take mPDF’s per-call orientation/format/margin overrides as arguments; change the document model between calls instead. |
$mpdf->SetHTMLHeader($html) / SetHTMLFooter($html) | header/footer via the Layout API | mPDF running HTML headers map to the NextPDF header/footer mechanism, not to inline HTML at the top of the body. |
$mpdf->Output($name, $dest) | $doc->output($name, OutputDestination::…) | mPDF destination characters (I/D/F/S) map to the OutputDestination enum (Inline/Download/file via save()/string via getPdfData()). |
$mpdf->Output('','S') | $doc->getPdfData() | Returns bytes. |
$mpdf->Output($path,'F') | $doc->save($path) | Writes to a file path. |
$mpdf->SetTitle($t) | $doc->setTitle($t) | Lands in the ISO 32000-2 §14 information dictionary / Extensible Metadata Platform (XMP) (iso32000_2_sec14#x1.x5.p5). |
$mpdf->SetProtection($perms,...) | $doc->setEncryption(...) (Security API) | Permissions are reader-cooperative, not access control — see Security notes. |
Code sample — Quick start
Section titled “Code sample — Quick start”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// mPDF:// $mpdf = new \Mpdf\Mpdf();// $mpdf->WriteHTML('<h1>Invoice</h1>');// $mpdf->Output('out.pdf', \Mpdf\Output\Destination::FILE);
// NextPDF — default page is A4 portrait:$doc = Document::createStandalone();$doc->setTitle('Invoice');$doc->addPage();$doc->writeHtml('<h1>Invoice</h1>');$doc->save(__DIR__ . '/out.pdf');
echo "Wrote out.pdf\n";Code sample — Production
Section titled “Code sample — Production”This example aligns with examples/04-text-and-fonts.php, the runnable backing
for the font-handling concepts in this guide. It uses an explicit page size,
margins, and a content body that exercises font selection.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\OutputDestination;use NextPDF\Core\Config;use NextPDF\Core\Document;use NextPDF\ValueObjects\Margin;use NextPDF\ValueObjects\PageSize;
// Equivalent of: new Mpdf(['format'=>'A4','margin_left'=>20, ...]).// Margin constructor order is (top, right, bottom, left) — NOT L,R,T,B.$config = new Config( pageSize: new PageSize(595.276, 841.890, 'A4'), margins: new Margin(16.0, 20.0, 16.0, 20.0), // top,right,bottom,left in points fontsDirectory: __DIR__ . '/fonts',);
$doc = Document::createStandalone($config);$doc->setTitle('Quarterly Report');$doc->addPage();
$html = <<<'HTML'<h1 style="font-family:'DejaVu Sans';color:#1E3A8A;">Quarterly Report</h1><p>Body text resolves through CSS font-family matching against the configuredfonts directory. mPDF's fontdata map has no direct analogue — register thefamily via CSS and the fonts directory instead.</p>HTML;
$doc->writeHtml($html);
// Equivalent of $mpdf->Output('report.pdf', Destination::DOWNLOAD):$doc->output('report.pdf', OutputDestination::Download);Edge cases & gotchas
Section titled “Edge cases & gotchas”$modeargument onWriteHTML. mPDF’sWriteHTML($html, $mode)(2 = CSS only, 1 = element) has no equivalent. Put CSS in the HTML you pass; there is no “write CSS then write body” sequence.- Per-
AddPageoverrides. mPDF letsAddPage()change format/orientation mid-document with arguments. NextPDFaddPage()takes no such arguments; model size changes through the document, not the page call. - Header/footer HTML. mPDF registers running headers as separate HTML fragments; do not paste them into the body. Map them to the NextPDF header/footer mechanism.
- Font names. mPDF normalizes font names through its
fontdata/core-font table. NextPDF matches the CSSfont-familyagainst the fonts directory; an mPDF alias that resolved silently may need an explicit@font-face/family. - Destination characters.
'I'/'D'/'F'/'S'are not accepted; use theOutputDestinationenum orsave()/getPdfData().
Performance
Section titled “Performance”writeHtml() is single-pass (ADR-001); peak memory tracks document size, not a
retained Document Object Model (DOM). Budget for this guide’s example:
wall_ms: 2000, peak_mb: 128. For long documents, split content across
addPage() calls rather than passing one giant string. This is the same guidance
that applies to mPDF’s $mode chunking, expressed through the page model.
Security notes
Section titled “Security notes”- Permissions are reader-cooperative.
SetProtection()→setEncryption()provides confidentiality, not access control: ISO permission bits depend on a cooperating reader. Do not present encryption as access control to users. - Metadata.
SetTitle()and document information land in the ISO 32000-2 §14 information dictionary / XMP (iso32000_2_sec14#x1.x5.p5); never store secrets there. - NextPDF does not execute in-document scripts; no mPDF directive changes that here.
Conformance
Section titled “Conformance”| Statement | Spec | Clause | reference_id |
|---|---|---|---|
| Fonts are written as embedded/subset font programs. | ISO 32000-2 | §9 | |
| Page format/margins map to the page boundary box. | ISO 32000-2 | §7 | |
| Title / protection metadata land in the info dictionary / XMP. | ISO 32000-2 | §14 | |
| Font matching / fallback is engine-specific. | CSS Fonts 4 | §5.5 |
NextPDF produces ISO 32000-2 content; it does not assert visual identity with mPDF. Re-review output whenever you change renderer.
Commercial context
Section titled “Commercial context”Not applicable. NextPDF core covers the mPDF migration path described here.
See also
Section titled “See also”Migration detail (R6 required sections)
Section titled “Migration detail (R6 required sections)”Who this is for
Section titled “Who this is for”Teams running mpdf/mpdf for server-side HTML-to-PDF. If your code is built
around new Mpdf([...]) + WriteHTML + Output (+ optional header/footer), the
verb mapping and config map
cover it.
In scope: the Mpdf constructor config array, WriteHTML/Output/AddPage,
headers/footers, fonts, protection, metadata. Out of scope: mPDF internal
classes and the Quick Response (QR)/barcode/watermark helper surface. Map those
to the corresponding NextPDF modules — Barcode, Graphics — which are not covered
here.
Compatibility map
Section titled “Compatibility map”Behavioral compatibility, not a drop-in shim: NextPDF does not provide an
Mpdf class shim. Rewrite every call site. Use the CSS support
matrix Verified rows for CSS-feature
expectations.
Constructor config-array map
Section titled “Constructor config-array map”The mPDF config keys below are confirmed against the upstream public repository
(mpdf/mpdf, development). No upstream documentation text is reproduced.
| mPDF config key | NextPDF | Notes |
|---|---|---|
mode | (no equivalent) | mPDF’s mode string ('utf-8', 'c', '+aCJK', …) selects font/script behavior. NextPDF is always Unicode; Chinese, Japanese, and Korean (CJK) text is handled by font selection, not a mode. Drop the key. |
format | Config->pageSize (PageSize value object (VO)) | Named formats become explicit point dimensions; arrays [w,h] map to a PageSize. |
orientation | swap PageSize width/height | No orientation flag; landscape means width > height. |
default_font_size | CSS base font-size | Set this in your base stylesheet, not a constructor key. |
default_font | CSS font-family / registered font | Set the default family through CSS / font registration. |
margin_left / margin_right / margin_top / margin_bottom | Config->margins (Margin VO) in points | Use one Margin value object; its constructor order is Margin(top, right, bottom, left) (verify against src/ValueObjects/Margin.php), not the mPDF key order. |
margin_header / margin_footer | header/footer offset via Layout API | Map these to the NextPDF header/footer configuration, not constructor keys. |
Font-handling delta
Section titled “Font-handling delta”- Single fonts directory. mPDF’s font directory list,
fontdatamap, and core-font fallback collapse toConfig->fontsDirectoryplus CSSfont-familymatching. - Always subset. NextPDF always subsets embedded fonts (ISO 32000-2 §9,
iso32000_2_sec9#x1.x45.p7); mPDF’s subset flags have no equivalent and are not needed. - Matching is engine-specific. Font matching and fallback differ by engine
(CSS Fonts 4 §5.5,
css_fonts_4#x1.x5.x5.x1.p13); an mPDF font alias may need an explicit@font-faceor exact family name. Re-baseline glyph rendering after migration. Substitution differences are expected, not defects.
Behavioral differences
Section titled “Behavioral differences”- Font substitution (see above) — the main visible delta.
- No
$modeonWriteHTML— pass complete HTML with inline CSS. - No per-
AddPageformat override — model size changes through the document. - Permissions are reader-cooperative (see Security notes).
- Independent layout engine — line wrap / pagination differs on dense content; re-baseline visual diffs.
These are documented behavioral differences, not defects in either engine.
Unsupported / no direct equivalent
Section titled “Unsupported / no direct equivalent”modeconstructor string — not modeled (always Unicode).- Per-
AddPage()format/orientation/margin arguments — not arguments in NextPDF. - mPDF
fontdatamap — replaced by fonts directory + CSS matching. - mPDF’s
'I'/'D'/'F'/'S'destination chars — replaced by theOutputDestinationenum +save()/getPdfData().
Safe migration sequence
Section titled “Safe migration sequence”- Add
nextpdf/corealongsidempdf/mpdf; keep mPDF installed for now. - Choose one low-risk document. Convert
new Mpdf([...])via the config map andWriteHTML/Outputvia the verb map. - Register the fonts the document uses in
Config->fontsDirectory, and add explicit@font-face/family declarations for any mPDF alias. - Generate both PDFs for the same input and visually diff them. Differences (font substitution, line wrap) are expected for independent engines — accept them per document.
- Map any header/footer HTML to the NextPDF header/footer mechanism.
- Repeat per document, lowest risk first; keep mPDF installed until the last cutover.
- Remove
mpdf/mpdffromcomposer.jsonafter the final cutover.
Testing the migration
Section titled “Testing the migration”- Snapshot mPDF output for representative documents before you change code (golden inputs; the bytes will differ).
- For each migrated document, assert acceptance with your own check (visual diff
- text-extraction). NextPDF’s font/HTML behavior is exercised by
examples/04-text-and-fonts.phpandexamples/08-html-basic.phpplus the coretests/Html/Font suites. Migration acceptance is document-specific and remains your responsibility.
- text-extraction). NextPDF’s font/HTML behavior is exercised by
- Add a regression test per migrated document.
Evidence / traceability
Section titled “Evidence / traceability”Every NextPDF behavioral statement on this page is backed by an in-repo test,
example, source signature, or architecture decision record (ADR), or, for
PDF-format properties, by the retrieval-augmented generation (RAG)-pinned ISO
32000-2 / CSS clauses in the front-matter citations: and the
Conformance table. mPDF behavior is asserted only as
“independent engine — expect documented differences”; this page claims no parity
that an in-repo artifact does not prove.
| NextPDF behavioral claim | In-repo evidence (path) |
|---|---|
WriteHTML() maps directly to Document::writeHtml(string $html): static. | src/Core/Concerns/HasTextOutput.php (writeHtml()); examples/08-html-basic.php. |
WriteFixedPosHTML(...) maps to writeHtmlCell(...). | src/Core/Concerns/HasTextOutput.php (writeHtmlCell()). |
createStandalone() default page is A4 portrait (595.276 × 841.890 pt). | src/Core/Config.php (default PageSize); tests/Unit/Core/DocumentCreateStandaloneAndConfigWithersEdgeCaseTest.php. |
Margin constructor order is (top, right, bottom, left). | src/ValueObjects/Margin.php (promoted-property order). |
Output destination is the NextPDF\Contracts\OutputDestination enum; 'I'/'D'/'F'/'S' are not accepted. | src/Contracts/OutputDestination.php (cases Inline/Download/File/String); tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php. |
Output('','S') → getPdfData(); Output($path,'F') → save($path). | src/Core/Concerns/HasOutput.php (getPdfData(), save(), output()). |
SetProtection() maps to setEncryption(...); permissions are reader-cooperative. | src/Core/Concerns/HasSecurity.php (setEncryption()); ISO 32000-2 §14 (front-matter citations:). |
SetTitle() → setTitle(); metadata lands in the info dictionary / XMP. | src/Core/Concerns/HasMetadata.php (setTitle()); tests/Unit/Core/Concerns/DocumentInfoMetadataSetterBaselineTest.php; ISO 32000-2 §14 (front-matter citations:). |
| Fonts are always embedded as subset programs. | tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php; examples/04-text-and-fonts.php; ISO 32000-2 §9 (front-matter citations:). |
| Font matching / fallback is engine-specific (substitution delta). | CSS Fonts 4 §5.5 (front-matter citations: + Conformance). |
writeHtml() is single-pass; peak memory tracks document size. | docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md. |
Rollback
Section titled “Rollback”Both packages stay installed until the final cutover, so per-call-site rollback
means reverting that call site to the mPDF path. After the final cutover,
rollback means restoring mpdf/mpdf and the prior code from version control. No
data migration is involved.
Performance considerations
Section titled “Performance considerations”See Performance. The single-pass model removes mPDF’s retained buffer cost. The new per-document cost is eager font resolution (step 3), which is cacheable through the fonts directory.
Common pitfalls
Section titled “Common pitfalls”- Keeping the
modekey and expecting CJK behavior from it; NextPDF drops it, and CJK is font selection. - Passing
WriteHTML($html, 2)(CSS-only mode); inline CSS instead. - Pasting header/footer HTML into the body.
- Expecting an mPDF alias to resolve to the same font without an explicit family.
- Expecting byte/pixel-identical output (independent engines — this guide never claims a drop-in or 100% compatibility).
- Treating the CSS support matrix as advisory; it is the Verified-feature authority.