Skip to content

Migrate from Dompdf to NextPDF

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.

Terminal window
composer require nextpdf/core:^3

Keep 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.

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.

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).

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.

DompdfNextPDFNotes
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 APIDompdf’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.
<?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";

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 that
is <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);
  • No two-phase render. Dompdf code that inspects state between render() and output(), such as reading the page count, has no NextPDF analogue at that exact boundary. Query the document after writeHtml() instead.
  • Encoding. NextPDF omits Dompdf’s $encoding parameter. Convert input to UTF-8 before writeHtml(). 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_php evaluates <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 before writeHtml().
  • 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.

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.

  • No remote fetch by stream context. NextPDF does not implement Dompdf’s setHttpContext() / enable_remote remote-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 that enable_remote carries.
  • No in-document code execution. The absence of an enable_php equivalent 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.
StatementSpecClausereference_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.

Not applicable. Core covers this HTML-to-PDF migration path.


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.

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.

Dompdf option (key / setter)NextPDFNotes
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/heightNextPDF 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->fontsDirectoryA 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 fontSet 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.
  • 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.

  • enable_php (in-document PHP) — intentionally absent.
  • setHttpContext() / enable_remote remote fetch — intentionally absent.
  • Public access to FrameTree / Canvas / Stylesheet — no public analogue.
  • dpi as 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.

  1. Add nextpdf/core alongside dompdf/dompdf. Do not remove Dompdf yet.
  2. Pick one low-risk document. Rewrite its call site with the verb mapping, and delete the render() call.
  3. 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.
  4. Convert option usage with the option map; recompute DPI-derived sizes into points.
  5. Pre-resolve remote and relative assets to absolute paths or data URIs to remove the resolution variable.
  6. Repeat per document, from lowest risk to highest. Keep both engines installed until the last call site is cut over.
  7. Remove dompdf/dompdf from composer.json only after the last cutover.
  • 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.php and the core tests/ 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.

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 claimIn-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).

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.

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.

  • 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_php templates, which must be refactored out.
  • Treating the CSS support matrix as advisory. It is the Verified-feature authority for what to expect.