Migrate from Dompdf to NextPDF
At a glance
Section titled “At a glance”This guide helps you move a Dompdf-based codebase that generates Portable Document Format (PDF) files from Hypertext Markup Language (HTML) to the NextPDF Html pipeline. Most call sites translate mechanically because both libraries follow the same broad flow: load HTML, render it, and emit a PDF. The real work is the option map and Cascading Style Sheets (CSS) support differences. NextPDF and Dompdf are independent engines, so a layout that Dompdf produced is compatible with the NextPDF result, not byte-identical to it. This guide covers verb mapping, option-key mapping, behavioral differences, and a safe migration sequence.
NextPDF support for an HTML/CSS feature does not guarantee that a Dompdf document reproduces pixel-for-pixel. The CSS support matrix is the authority for Verified features. This guide describes behavior; it does not claim visual equivalence.
Install
Section titled “Install”composer require nextpdf/core:^3Keep dompdf/dompdf installed during the transition. The safe migration
sequence runs both engines side by side until each
call site is cut over. Remove it once the cutover is complete.
Conceptual overview
Section titled “Conceptual overview”Dompdf’s Dompdf object is a single facade that owns the Document Object Model
(DOM), the stylesheet, the frame tree, and the canvas. NextPDF splits these
concerns: a NextPDF\Core\Document owns the page model and output, and
writeHtml() drives the HTML pipeline. There is no separate two-step “render,
then output” phase. writeHtml() lays out content as it writes it, and you emit
the document with save(), output(), or getPdfData().
The page content NextPDF writes is ISO 32000-2 content-stream painting (ISO
32000-2 §8, iso32000_2_sec8#x1.x3.p14). The page geometry controlled by the
paper-size option maps to the page object MediaBox (ISO 32000-2 §7,
iso32000_2_sec7#x1.x104.p10). These are engine fundamentals shared by any
conforming writer. The layout algorithm that turns CSS into that content is
NextPDF’s own, and it differs from Dompdf’s; see Behavioral
differences.
API surface
Section titled “API surface”The NextPDF Html application programming interface (API) is documented in the
Html module reference, which is auto-generated from
PHPDoc. The entry points used below are Document::createStandalone(),
Document::writeHtml(string $html): static,
Document::writeHtmlCell(...), Document::output(?string, OutputDestination),
Document::save(string $path): void, Document::getPdfData(): string, and the
NextPDF\Core\Config value object (pageSize, margins, fontsDirectory).
API verb mapping
Section titled “API verb mapping”The Dompdf public API names below are confirmed against the upstream public
repository (dompdf/dompdf, master); see the in-repo
_source-sidecar-upstream-api.md provenance sidecar. No upstream documentation
text is reproduced.
| Dompdf | NextPDF | Notes |
|---|---|---|
new Dompdf($options) | Document::createStandalone($config) | Dompdf takes an Options object; NextPDF takes a NextPDF\Core\Config. Use DocumentFactory for long-lived workers instead of createStandalone(). |
$dompdf->loadHtml($html, $encoding) | $doc->writeHtml($html) | NextPDF treats input as UTF-8. Transcode non-UTF-8 input before the call rather than passing an encoding argument. |
$dompdf->loadHtmlFile($file) | $doc->writeHtml(file_get_contents($file)) | NextPDF has no file-loading variant. Read the file yourself so resource policy stays in your code. |
$dompdf->setPaper($size, $orientation) | ConfigpageSize (a PageSize value object) | See the option map. |
$dompdf->render() | (implicit) | NextPDF lays out during writeHtml(); there is no separate render phase. Remove the render() call. |
$dompdf->output() | $doc->getPdfData() | Returns the PDF bytes. |
$dompdf->stream($name, $opts) | $doc->output($name, OutputDestination::Download) | NextPDF selects the destination with the OutputDestination enum. |
$dompdf->setBasePath($p) / setProtocol() / setBaseHost() | (resource resolution differs) | NextPDF resolves relative resources against the document working set, not a base path/protocol triplet; see Behavioral differences. |
$dompdf->addInfo($label, $value) | $doc->setTitle() / setAuthor() / metadata API | Dompdf’s free-form info pairs map to typed metadata setters (ISO 32000-2 §14 document information, iso32000_2_sec14#x1.x5.p5). |
$dompdf->setHttpContext($ctx) | (no equivalent) | NextPDF does not fetch remote resources through a stream context; see Unsupported / no direct equivalent. |
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;
// Dompdf:// $dompdf = new Dompdf();// $dompdf->loadHtml('<h1>Invoice</h1>');// $dompdf->setPaper('A4', 'portrait');// $dompdf->render();// file_put_contents('out.pdf', $dompdf->output());
// NextPDF — the createStandalone() default page size 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 mirrors examples/08-html-basic.php, the runnable backing for this guide,
with an explicit non-default paper size and margins. It matches a Dompdf
setPaper() call plus an Options margin configuration.
<?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: $dompdf->setPaper('letter','portrait') + margin options.// US Letter portrait = 612 x 792 pt.// Margin constructor order is (top, right, bottom, left) — all 0.5in here.$config = new Config( pageSize: new PageSize(612.0, 792.0, 'Letter'), margins: new Margin(36.0, 36.0, 36.0, 36.0), // top,right,bottom,left; 0.5in in points);
$doc = Document::createStandalone($config);$doc->setTitle('Quarterly Report');$doc->setAuthor('Finance');$doc->addPage();
$html = <<<'HTML'<h1 style="color:#1E3A8A;">Quarterly Report</h1><p>This report renders through the NextPDF Html pipeline. The CSS subset thatis <strong>Verified</strong> for production is the support-matrix authority,not this page.</p><table border="1"> <tr><th>Region</th><th>Total</th></tr> <tr><td>EMEA</td><td>1,204</td></tr></table>HTML;
$doc->writeHtml($html);
// Equivalent of $dompdf->stream('report.pdf'):$doc->output('report.pdf', OutputDestination::Download);Edge cases & gotchas
Section titled “Edge cases & gotchas”- No two-phase render. Dompdf code that inspects state between
render()andoutput(), such as reading the page count, has no NextPDF analogue at that exact boundary. Query the document afterwriteHtml()instead. - Encoding. NextPDF omits Dompdf’s
$encodingparameter. Convert input to UTF-8 beforewriteHtml(). Passing Latin-1 bytes produces mojibake, not an error. render()left in place. A leftover$dompdf->render()-style call has no NextPDF method and fails with a fatal “undefined method” error. Delete it during the cutover; do not stub it.- Inline PHP. Dompdf’s
enable_phpevaluates<script type="text/php">. NextPDF has no in-document PHP execution by design because it is an injection surface. Move that logic into your PHP beforewriteHtml(). - Relative-resource resolution. Dompdf resolves
<img src>against the base path/protocol/host triple. NextPDF resolves against the document working set. During migration, pass absolute paths or pre-resolved data URIs to remove that variable.
Performance
Section titled “Performance”writeHtml() lays out in a single streaming pass, as described in architecture
decision record ADR-001. There is no intermediate frame-tree object retained
after layout, so peak memory tracks document size rather than DOM node count.
The performance budget for this guide’s example is wall_ms: 2000, peak_mb: 128. For large documents, chunk the HTML across addPage() boundaries rather
than building one multi-megabyte string.
Security notes
Section titled “Security notes”- No remote fetch by stream context. NextPDF does not implement Dompdf’s
setHttpContext()/enable_remoteremote-fetch path. Resolve and validate remote assets in your application, then pass bytes or data URIs. This removes the server-side request forgery (SSRF) surface thatenable_remotecarries. - No in-document code execution. The absence of an
enable_phpequivalent is a deliberate hardening, not a gap. - Document metadata you set through the typed setters is written to the ISO
32000-2 §14 information dictionary / Extensible Metadata Platform (XMP)
(
iso32000_2_sec14#x1.x5.p5). Do not place secrets there.
Conformance
Section titled “Conformance”| Statement | Spec | Clause | reference_id |
|---|---|---|---|
| Page content is content-stream painting in the opaque/transparent model. | ISO 32000-2 | §8 | |
| Paper size maps to the page object boundary box. | ISO 32000-2 | §7 | |
| HTML fonts are written as embedded/subset font programs. | ISO 32000-2 | §9 | |
| White-space / line-breaking processing is engine-specific. | CSS Text 3 | §6.5 |
NextPDF produces ISO 32000-2 content. It does not claim that a migrated Dompdf document is visually identical. Re-review output whenever you change renderers.
Commercial context
Section titled “Commercial context”Not applicable. Core covers this HTML-to-PDF migration path.
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”Use this if your team runs dompdf/dompdf for server-side HTML-to-PDF and wants
the NextPDF engine. If you only call loadHtml / setPaper / render /
output, the verb mapping covers your whole surface.
In scope: the Dompdf facade verbs, the Options keys, CSS-feature parity
expectations, resource resolution, and metadata. Out of scope: Dompdf’s internal
FrameTree/Canvas/Stylesheet objects. NextPDF has no public analogues, so
do not migrate code that reaches into them; replace it with the public API.
Compatibility map
Section titled “Compatibility map”Coverage means behavioral compatibility, not a drop-in shim. NextPDF has no
Dompdf class shim (unlike the TCPDF path; see
/migration/tcpdf-compat/).
Rewrite every call site using the verb mapping. The CSS
support matrix Verified rows govern
CSS-feature expectations entirely. This guide does not restate per-property
status.
Option and configuration map
Section titled “Option and configuration map”| Dompdf option (key / setter) | NextPDF | Notes |
|---|---|---|
default_paper_size / setDefaultPaperSize() ; setPaper($size,...) | Config->pageSize (PageSize VO) | Named sizes become explicit point dimensions. new PageSize(595.276, 841.890, 'A4') is the createStandalone() default. |
default_paper_orientation / setDefaultPaperOrientation() | swap PageSize width/height | NextPDF has no orientation flag. A landscape page is a PageSize with width > height. |
dpi / setDpi() | (not a global knob) | NextPDF works in PDF points (1/72 in). Image sizing is per-image, not a document dots per inch (DPI) multiplier. Recompute fixed pixel sizes into points. |
enable_remote / setIsRemoteEnabled() | (no equivalent — by design) | Resolve remote assets in your code; see Security notes. |
enable_html5_parser / setIsHtml5ParserEnabled() | (always parses HTML) | No toggle; the parser is the pipeline. |
enable_php / setIsPhpEnabled() | (no equivalent — by design) | In-document PHP is not supported. Move logic out of the template. |
font_dir / setFontDir() | Config->fontsDirectory | A single fonts directory string. |
chroot | (resolve in app) | NextPDF does not accept a filesystem jail option. Validate paths before passing bytes. |
default_font / setDefaultFont() | CSS font-family / registered font | Set the default in your base stylesheet or font registration, not a global option. |
enable_font_subsetting / setIsFontSubsettingEnabled() | (always subsets) | NextPDF always subsets embedded fonts (ISO 32000-2 §9, iso32000_2_sec9#x1.x45.p7). There is no “off”; a flag-off Dompdf path has no equivalent and is not needed. |
Behavioral differences
Section titled “Behavioral differences”- Layout engine. Dompdf and NextPDF use independent CSS layout
implementations. White-space collapsing and line breaking are specified, but
remain engine-sensitive (CSS Text 3 §6.5,
css_text_3#x1.x6.x5.p20). Expect line-wrap and pagination differences in dense text. Re-baseline visual diffs after migration. - Render seam. No
render()/output()two-phase boundary (see Edge cases). - Resource resolution. Base-path/protocol/host handling differs from the document working set.
- DPI model. PDF points differ from Dompdf’s DPI multiplier.
- Metadata. Free-form
addInfo()pairs differ from typed setters (ISO 32000-2 §14,iso32000_2_sec14#x1.x5.p5).
These are documented behavioral differences, not defects in either engine.
Unsupported / no direct equivalent
Section titled “Unsupported / no direct equivalent”enable_php(in-document PHP) — intentionally absent.setHttpContext()/enable_remoteremote fetch — intentionally absent.- Public access to
FrameTree/Canvas/Stylesheet— no public analogue. dpias a document-global multiplier — not modeled.
Code that depends on these does not “migrate”. Remove it or re-express it in application code using the rows above.
Safe migration sequence
Section titled “Safe migration sequence”- Add
nextpdf/corealongsidedompdf/dompdf. Do not remove Dompdf yet. - Pick one low-risk document. Rewrite its call site with the verb
mapping, and delete the
render()call. - Generate both PDFs from the same input and visually diff them. Treat differences as expected because the engines are independent, and decide acceptance per document.
- Convert option usage with the option map; recompute DPI-derived sizes into points.
- Pre-resolve remote and relative assets to absolute paths or data URIs to remove the resolution variable.
- Repeat per document, from lowest risk to highest. Keep both engines installed until the last call site is cut over.
- Remove
dompdf/dompdffromcomposer.jsononly after the last cutover.
Testing the migration
Section titled “Testing the migration”- Snapshot the Dompdf output of representative documents before changing code. Use golden inputs, not golden bytes, because the bytes will differ.
- For each migrated document, run the NextPDF output through your own
acceptance check, such as visual diff or text-extraction assertions.
NextPDF’s own HTML pipeline behavior is covered by
examples/08-html-basic.phpand the coretests/Html suite. Migration acceptance is document-specific, and you are responsible for asserting it. - Add a regression test for each migrated document so a future engine update is caught.
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 frontmatter citations: and the
Conformance table. Dompdf behavior is asserted only as
“independent engine — expect documented differences”. This page claims no parity
unless an in-repo artifact proves it.
| NextPDF behavioral claim | In-repo evidence (path) |
|---|---|
createStandalone() default page is A4 portrait (595.276 × 841.890 pt). | src/Core/Config.php (default PageSize(595.276, 841.890, 'A4')); tests/Unit/Core/DocumentCreateStandaloneAndConfigWithersEdgeCaseTest.php (createStandaloneWithNullConfigBuildsDocumentWithA4Defaults). |
writeHtml() lays out in a single streaming pass; no retained DOM after layout. | docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md; src/Core/Concerns/HasTextOutput.php (writeHtml()). |
writeHtml() auto-creates the first page when none exists. | tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php (writeHtmlAutoCreatesFirstPageWhenNoPagesExist). |
output() / save() / getPdfData() are the emission verbs (no render/output two-phase). | src/Core/Concerns/HasOutput.php (output(), save(), getPdfData()); tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php. |
Output destination is the NextPDF\Contracts\OutputDestination enum (Inline/Download/File/String). | src/Contracts/OutputDestination.php; tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php. |
| HTML fonts are always written as embedded/subset programs. | tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php (recordUsedCharactersAffectsFontSubsetting); ISO 32000-2 §9 (front-matter citations:). |
Typed metadata setters (setTitle/setAuthor) replace free-form addInfo(). | src/Core/Concerns/HasMetadata.php (setTitle(), setAuthor()); tests/Unit/Core/Concerns/DocumentInfoMetadataSetterBaselineTest.php. |
| End-to-end HTML pipeline (this guide’s runnable backing). | examples/08-html-basic.php; core tests/Unit/Html/ suite. |
| White-space / line-breaking is engine-specific (layout delta). | CSS Text 3 §6.5 (front-matter citations: + Conformance). |
Rollback
Section titled “Rollback”Because both packages stay installed until the final cutover, rollback for an
unconverted call site means reverting that call site to the Dompdf path. After
the final cutover, rollback means restoring dompdf/dompdf and the prior call
site from version control. No data migration is involved; only code changes.
Performance considerations
Section titled “Performance considerations”See Performance. The single-pass model means migration does not introduce a frame-tree retention cost. The main per-document cost change is eager asset resolution from step 5, which you can cache.
Common pitfalls
Section titled “Common pitfalls”- Leaving
render()in place, which causes a fatal undefined method. - Passing non-UTF-8 bytes after dropping
$encoding, which causes silent mojibake. - Expecting byte-identical or pixel-identical output from independent engines. This guide never claims a drop-in or 100% compatibility.
- Relying on
enable_phptemplates, which must be refactored out. - Treating the CSS support matrix as advisory. It is the Verified-feature authority for what to expect.