html-pdf ironpress_php

v0.4.4 · MIT · PHP 8.1+

PHP lays the page out and returns PDF bytes.

html-pdf is the Homebrew formula for ironpress_php. HTML and Markdown go in. PDF bytes come out. Layout runs in ironpages 2.0, the renamed fork of ironpress 1.7.0. The project does not use Chromium, wkhtmltopdf, Playwright, or Puppeteer.

Stamp coordinates measured from the top edge A broken vertical axis starts at 0 on the top edge. A tick sits in a box at y 146.2. The word Doe sits on a baseline at y 230.8. The gap in the axis is only so the tick stays readable. top edge 0 146.2 230.8 Doe

PdfStamper measures y in points from the top edge. These calls are the example in the project README. The axis is broken so the 10.8 pt tick is large enough to see.

check(1, 56.2, 146.2, 10.8)
Queues a tick inside a 10.8 pt box. x and y are the top-left corner. The default stroke is 1.5 pt.
text(1, 59.0, 230.8, 'Doe')
Queues Doe on the baseline. y is that baseline. Size defaults to 9 pt Helvetica. The drawing uses Helvetica because that is the built-in face.

Install with Homebrew.

Tap first, then install. The formula’s homepage is the source repository.

  1. brew tap dickwu/tap
  2. brew install dickwu/tap/html-pdf
  3. php -m | grep ironpress_php

The formula detects installed Homebrew PHP versions, builds a matching extension for each minor version, and writes $(brew --prefix)/etc/php/<php-minor>/conf.d/ext-ironpress_php.ini. On macOS the build produces a dylib; elsewhere it produces a shared library. Either file is installed as ironpress_php.so. Reinstall html-pdf after installing another Homebrew PHP version. Restart long-running PHP processes after install or upgrade.

Three tools in the extension.

Render a document, stamp a PDF you already have, or append pages. Each one is a separate API.

ironpress_html_to_pdf

ironpress_html_to_pdf returns PDF bytes for an HTML string. ironpress_html_to_pdf_file writes them to a path. Markdown has ironpress_markdown_to_pdf, ironpress_convert_markdown_file, and ironpress_convert_file reads an HTML file. Ironpress\HtmlConverter holds page size, margins, landscape, header, footer, fonts, and sanitization, then convert or convertMarkdown uses them. The plain functions use the same defaults, including table-cell metrics. measureSentinelTops lays the HTML out without writing a PDF and returns the top y, in points from the top of the page content box, of every empty block whose fixed height and solid #RRGGBB background match the signature you pass. The document has to fit on one page.

$pdf = ironpress_html_to_pdf('<h1>Hello</h1><p>World</p>');
file_put_contents(__DIR__ . '/hello.pdf', $pdf);

$converter = new Ironpress\HtmlConverter();
$converter->pageSize('letter');
$converter->landscape(true);
$converter->margin(36.0);
$converter->header('Report');
$converter->footer('Page {page} of {pages}');
$pdf = $converter->convert('<h1>Quarterly Report</h1>');

Ironpress\PdfStamper

Stamp text and tick marks onto a PDF you already have, such as a form, without changing the original page content. toPdf() returns the stamped bytes and leaves the template and the queue in place. reset() drops the queue so the same template can be filled again. Added fonts stay through reset() and are embedded once per PDF, only when a text uses them. Only TrueType outlines (glyf) are supported. .otf (CFF) fonts, font collections, and fonts whose licence forbids embedding (OS/2 fsType restricted) are refused. Text is WinAnsi. A missing character throws instead of printing a question mark.

$stamper = new Ironpress\PdfStamper(file_get_contents('form.pdf'));
$stamper->pageCount();
$stamper->text(1, 59.0, 230.8, 'Doe');
$stamper->text(1, 127.0, 655.0, '2026-10-05', 9.0, 'Helvetica', 'center');
$stamper->check(1, 56.2, 146.2, 10.8);
$stamper->addFont('Signature', file_get_contents('Allura-Regular.ttf'));
$stamper->text(1, 236.9, 463.1, 'Jane Doe', 16.0, 'Signature');
file_put_contents('filled.pdf', $stamper->toPdf());
$stamper->reset();

Ironpress\PdfMerger

Append the pages of other PDFs onto one you just made or already have. Each page keeps its size, rotation, fonts, and images, including attributes it inherits from its page tree. Bookmarks, form-field trees, named destinations, tagged-PDF structure, and optional content of appended PDFs are dropped. Widgets still show but are no longer fillable. Identical fonts or images in several inputs are stored once per input. A rejected input leaves the merger as it was. ironpress_pdf_merge does the same in one call, and the message names the failing index (PDF #2: ...). Errors extend Ironpress\PdfMergeException. getCode() is EMPTY, NOT_PDF, CORRUPT, NO_PAGES, PASSWORD_PROTECTED, OWNER_RESTRICTED, INPUT_TOO_LARGE, or TOO_MANY_PAGES. PHP code cannot construct those exception classes.

$merger = new Ironpress\PdfMerger(ironpress_html_to_pdf($html));
$merger->maxPages(500);
$merger->maxInputBytes(50 << 20);
foreach ($attachments as $pdf) {
    try {
        $merger->append($pdf);
    } catch (Ironpress\EncryptedPdfException $e) {
        // password-protected, or owner-restricted
    } catch (Ironpress\InvalidPdfException $e) {
        // empty, not a PDF, corrupt, or no pages
    }
}
file_put_contents('out.pdf', $merger->toPdf());

Defaults that change the PDF.

Page size
HtmlConverter starts on A4. pageSize() accepts a4, letter, or legal. customPageSize() takes a width and height in points. landscape(true) swaps the sides when the height is greater than the width.
Header and footer
Both take a string. The README’s footer example is Page {page} of {pages}.
Sanitize
HTML sanitization is on. Call sanitize(false) only when the input is trusted.
Remote URLs
Remote resource loading is off unless you build with the remote feature: cargo build --release --features remote.
Table cells
Legacy table-cell metrics are on, matching ironpress-php 0.2.x: collapsed borders take no layout space, only a cell’s first-top and last-bottom child margins are kept, and tables get no default border-spacing. legacyTableCellMetrics(false) switches to Chromium-like cell metrics. The plain ironpress_* functions use the same default.
Merge limits
5000 pages, and 128 MiB per input. allowOwnerRestricted is off, so an owner-restricted PDF is rejected unless you opt in. Password-protected PDFs are rejected.
Fonts on the converter
addFont($name, $ttf) rejects an empty name, empty bytes, and a font over 50 MB. clearFonts() drops the ones already added.
Version
ironpress_version() returns the extension version string.

Build it into the current PHP.

Requirements for a source build: PHP 8.1+, php-config and the PHP development headers, a Rust toolchain, and Clang. cargo-php 0.2.1 installs the extension. The README also documents a manual cargo build --release and how to load the built library for tests.

  1. cargo install cargo-php --version 0.2.1 --locked
  2. cargo php install --release --yes
  3. php -m | grep ironpress

On macOS, if cargo-php fails to install because the PHP symbols are unresolved during linking, install it with PHP’s extension linker mode:

RUSTFLAGS='-C link-arg=-Wl,-undefined,dynamic_lookup' cargo install cargo-php --version 0.2.1 --locked

Add -n when a Homebrew build of the extension is already loaded from conf.d. Otherwise PHP warns that the module is loaded twice and keeps the installed one. The formula itself is in dickwu/homebrew-tap. Release v0.4.4 was published on October 26, 2026.

Questions

What does the html-pdf formula install?

A PHP extension named ironpress_php. Homebrew builds one for each installed Homebrew PHP minor version and writes $(brew --prefix)/etc/php/<php-minor>/conf.d/ext-ironpress_php.ini. The formula homepage is https://github.com/dickwu/html-pdf.

How do I install it?

Run brew tap dickwu/tap, then brew install dickwu/tap/html-pdf. Confirm the module with php -m | grep ironpress_php.

Does it render with a browser?

No. Ironpress renders through its Rust layout engine. Do not add browser, wkhtmltopdf, Chromium, Playwright, or Puppeteer runtime dependencies.

Which PHP versions does it support?

PHP 8.1 or newer. A source build needs php-config, the PHP development headers, a Rust toolchain, and Clang. The Homebrew formula depends on php, and on llvm and rust at build time. Reinstall html-pdf after installing another Homebrew PHP version. Restart long-running PHP processes after install or upgrade.

Is HTML sanitized, and are remote URLs loaded?

HTML sanitization is enabled by default. Call sanitize(false) only when the input is trusted. Remote resource loading is not enabled by default. The extension only compiles remote URL support when built with the remote feature: cargo build --release --features remote.

How are stamp coordinates measured?

Ironpress\PdfStamper writes text and tick marks on top of an existing PDF without altering the original page content. Coordinates are PDF points with y measured from the top edge of the page. text() takes the baseline. check() takes the top-left corner of the box. Text uses Helvetica, Helvetica-Bold, or a TrueType font added with addFont(), always with WinAnsi encoding. A character outside that repertoire, or missing from an added font, throws instead of printing a question mark.

What does merging keep or drop?

Ironpress\PdfMerger appends pages and keeps each page's size, rotation, fonts, and images, including attributes inherited from its page tree. Bookmarks, form-field trees, named destinations, tagged-PDF structure, and optional content of appended PDFs are dropped. Widgets still show but are no longer fillable. Identical fonts or images in several inputs are stored once per input. Password-protected PDFs are rejected. PDFs that open without a password but carry owner restrictions are rejected unless you call allowOwnerRestricted(true). The default limit is 5000 pages and 128 MiB per input. A rejected input leaves the merger unchanged.

Where is the source?

The source repository and the formula homepage are https://github.com/dickwu/html-pdf. The license is MIT. The formula version is 0.4.4, published on October 26, 2026.