ScreenshotNeo

BlogHTML to image & PDF

Convert HTML to PDF in PHP: Video Tutorial

Generate a PDF from HTML in PHP with Dompdf. Follow the runnable example, learn about page settings and security, and see when another renderer may fit better.

By the ScreenshotNeo team4 October 20269 min read

To convert HTML to PDF in PHP, use a rendering library such as Dompdf: install it with Composer, pass it HTML with loadHtml(), optionally set the paper size, call render(), then stream or save the PDF. This tutorial uses Dompdf for a compact, runnable example. HTML and CSS support differs between PDF renderers, so check the selected library against your document and server requirements.

Video tutorial and what you will build

The workflow is: create or load the HTML, render it as PDF, then either return the file to a browser or save it on the server. The example below builds the HTML in PHP so it can run without a template framework. Replace the sample content with your own trusted, escaped data.

For a video walkthrough, see Convert HTML to PDF in PHP. The code below follows the documented Dompdf sequence and is useful when you want to copy and adapt each step.

1. Install Dompdf

In a Composer-managed PHP project, run:

composer require dompdf/dompdf

Dompdf’s project lists PHP 7.1 or newer and the DOM extension, along with its font and SVG dependencies. MBString and GD are relevant to processing and some features. Verify the requirements for the precise Dompdf release you install and the PHP runtime used by your web server; the command-line PHP configuration and web-server PHP configuration can differ.

If your project does not use Composer, install the library and load its autoloader according to the project documentation. For most applications, Composer is the simplest way to resolve and update dependencies.

2. Generate and stream a PDF

Create a PHP endpoint such as invoice.php in the Composer project directory:

<?php
require __DIR__ . '/vendor/autoload.php';

use Dompdf\Dompdf;
use Dompdf\Options;

$options = new Options();
// Remote fetching is disabled by default. Keep it disabled unless needed.
$options->set('isRemoteEnabled', false);

$dompdf = new Dompdf($options);

$html = '<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Invoice</title>
  <style>
    body { font-family: DejaVu Sans, sans-serif; font-size: 12px; color: #222; }
    h1 { color: #174ea6; }
    table { width: 100%; border-collapse: collapse; }
    th, td { border: 1px solid #bbb; padding: 8px; text-align: left; }
    @page { margin: 24mm 18mm; }
  </style>
</head>
<body>
  <h1>Invoice 1042</h1>
  <p>Prepared for Example Company</p>
  <table>
    <thead><tr><th>Item</th><th>Amount</th></tr></thead>
    <tbody><tr><td>Consulting</td><td>$500.00</td></tr></tbody>
  </table>
</body>
</html>';

$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();

// true asks the browser to download the PDF; false opens it inline.
$dompdf->stream('invoice-1042.pdf', ['Attachment' => true]);

Save the file and request it through your PHP web server. The response should be a PDF download. If you want the browser to display the PDF instead, set Attachment to false. Do not print debug text, whitespace, or a byte-order mark before the PDF response: it can corrupt the file headers.

3. Save the PDF instead of streaming it

For background jobs or generated documents that must be retained, use output() and write the returned bytes to a controlled path:

<?php
// After loadHtml(), setPaper(), and render():
$pdfBytes = $dompdf->output();
$outputPath = __DIR__ . '/generated/invoice-1042.pdf';

if (file_put_contents($outputPath, $pdfBytes) === false) {
    throw new RuntimeException('Could not write the PDF file.');
}

Ensure the destination directory exists and is writable by the PHP process. Avoid using user-provided paths or filenames directly; choose a server-controlled location and validate any identifier used to construct a filename.

4. Set page size, orientation, and margins

setPaper() accepts a paper name and orientation. Common choices include A4 or letter, with portrait or landscape. CSS @page can set print margins, as in the example. Check the rendered output when changing paper sizes because pagination and line wrapping depend on the available page dimensions.

$dompdf->setPaper('letter', 'landscape');

For reports that need different page styling or page breaks, use print-oriented CSS supported by your chosen renderer, and inspect the resulting PDF. PDF engines implement subsets of browser HTML and CSS; do not assume a browser-perfect match.

5. Add images, stylesheets, and fonts safely

Dompdf supports external stylesheets and images subject to its configuration and access restrictions. Remote access is disabled by default. If the document must fetch remote resources, Dompdf documentation says to enable isRemoteEnabled and provide cURL or allow_url_fopen support:

$options->set('isRemoteEnabled', true);

Enable this only when necessary. A renderer that fetches URLs from untrusted HTML can make requests to unintended destinations. Prefer known, trusted asset URLs, restrict what your application accepts, and configure local file access narrowly. For local assets, use paths that the renderer is permitted to read and confirm that the PHP process can access them.

Use fonts available to Dompdf and test accented characters and symbols in the actual PDF. Missing glyphs often show up as blank squares or replacement characters. Keep images at a sensible resolution and dimensions; very large source images increase memory and rendering time without necessarily improving the printed result.

6. Protect user data and HTML

If HTML contains user-submitted values, escape those values for HTML before inserting them. Treat user-controlled markup, CSS, and resource URLs as untrusted. mPDF’s manual explicitly warns against passing outside HTML/CSS to its renderer without additional vetting and sanitization; the same security discipline is appropriate for any server-side document renderer.

  • Build document structure from application templates rather than accepting arbitrary complete HTML where possible.
  • Escape text values, and validate links, image sources, and other resource references.
  • Keep remote fetching off unless the document needs it.
  • Limit input size and avoid rendering untrusted CSS or markup that can consume excessive resources.
  • Store generated PDFs outside executable application paths and apply the access controls required for the document’s contents.

7. Choose a PHP HTML-to-PDF renderer

Dompdf is one option, not a universal fit. Compare the renderer with the document’s CSS needs, PHP version and extensions, deployment constraints, and required PDF features. The available project documentation describes different approaches; the sources used here do not report a common benchmark or a cross-library rendering test.

Renderer Approach and documented fit Consider when
Dompdf PHP-based renderer, mostly CSS 2.1 compliant with some CSS3 support; supports selected print rules, stylesheets, and images subject to configuration. You need a Composer-installable PHP library for conventional invoices, reports, and print-style pages.
mPDF Accepts HTML and custom tags as well as PHP commands; documents headers, footers, page numbering, tables of contents, barcodes, and print-oriented color handling. Your PDF needs its documented print features. Check the selected release’s PHP and extension requirements and sanitize outside markup.
tc-lib-pdf (TCPDF’s current generation) Renders a subset of HTML/CSS with its own engine; its HTML cell can flow across pages. You want its PDF library workflow and can work within the HTML/CSS subset it supports.
wkhtmltox integration Interfaces with a QtWebKit-based library that renders HTML into PDF and image formats. Your deployment can accommodate the external library and its runtime requirements.

Before switching libraries, make a representative sample document with your real fonts, tables, images, and page breaks. Verify the selected release’s current compatibility and installation requirements. Rendering features and compatibility can change between versions.

8. Performance, reliability, and cost

PDF generation consumes server CPU and memory, with larger documents and images generally requiring more work. The source material does not provide comparable renderer benchmarks, so measure with representative documents in your own deployment instead of relying on an assumed speed ranking.

  • Bound the workload: Set application-level limits on input size, number of pages, images, and concurrent jobs.
  • Reuse stable inputs: If a document is identical for a period, store the generated output or cache it using a key that includes all content and options affecting the PDF.
  • Handle failures: Log the renderer exception and request identifier, but avoid logging sensitive document contents. Return a useful error to the caller and retry only when the failure is plausibly transient.
  • Use background work for heavy documents: If PDF generation makes web requests slow, queue the job and expose a status or download flow appropriate to your application.
  • Plan cost around infrastructure: A PHP library has no per-call price in the cited project material, but it uses your compute, storage, and operational capacity. An external rendering dependency also adds deployment and maintenance work.

9. Troubleshooting common problems

Symptom Likely cause What to check or change
Class Dompdf\Dompdf not found The package or Composer autoloader is missing, or the script is running from a different project directory. Run Composer in the application project and require the correct vendor/autoload.php path.
Missing DOM, MBString, or GD errors A PHP extension required by the selected release is absent from the active runtime. Check extensions for the web-server PHP version as well as CLI PHP; install/enable the needed extension and restart the relevant service.
Remote image or stylesheet is absent Remote access is off, URL access is unavailable, or the resource is blocked or inaccessible. Prefer local trusted assets; if remote loading is required, review Dompdf’s access settings, cURL or allow_url_fopen, URL validity, and network access.
Local image cannot be read The path is outside allowed locations, malformed, or unreadable by the PHP process. Use a valid permitted path and verify file permissions and Dompdf local access configuration.
Layout differs from the browser The document relies on CSS or HTML features outside the renderer’s supported subset. Check the library’s compatibility documentation, simplify or adapt print CSS, or evaluate another renderer against a sample document.
Text glyphs are missing The selected font lacks those glyphs or is not available to the renderer. Choose and configure a font with the needed character coverage; test non-Latin text and symbols in the generated output.
PDF is corrupt or the browser shows a blank response Output was sent before PDF headers, PHP emitted a warning, or an exception interrupted rendering. Remove debug output and leading whitespace, inspect server logs, and ensure the endpoint handles errors before streaming bytes.
Memory exhaustion or slow requests The HTML, images, page count, or concurrency exceeds available resources. Reduce image dimensions and input size, cap workload, allocate suitable resources, or move generation to a background worker.

Or skip the browser setup

If your goal is a PDF of a rendered website page, ScreenshotNeo can return a PDF from one GET request. It is a website screenshot API and MCP server; it renders a URL rather than replacing a PHP library for arbitrary HTML strings. The ScreenshotNeo documentation has the API details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o page.pdf

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed, and the response includes X-Page-Verdict and X-Billed headers. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. ScreenshotNeo offers PNG, JPEG, WebP, or PDF output and can also capture supplied HTML/CSS as an image.

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com", "format": "pdf"},
    timeout=90,
)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
  format: 'pdf'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('page.pdf', Buffer.from(await res.arrayBuffer())));

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

FAQ

Can Dompdf convert a complete website exactly as Chrome prints it?

Do not assume so. Dompdf documents a mostly CSS 2.1 implementation with selected CSS3 support, not full modern browser parity. Test the specific layout and consider another rendering approach if it depends on unsupported features.

Should I use a PHP library or a website screenshot API?

Use a PHP library when your application generates documents from its own HTML templates or data. Use a URL capture API when the input is a rendered website URL and a managed capture fits the job.

Can I safely render HTML submitted by users?

Only after careful validation and sanitization. User-controlled markup, styles, and resource URLs can affect security and resource use; prefer controlled templates and escaped values.

Does changing the PDF filename change the PDF contents?

No. The filename passed to stream() controls the suggested download name. Page content and layout come from the HTML, CSS, paper settings, and renderer behavior.