phil / paged-media
PDF documents behind one API: a native writer with embedded and subset TrueType fonts, images, lines and rectangles and compressed streams, with no dependency, fillable form fields and printed templates, and a Gotenberg 9 client for PDF stages, PDF/A and PDF/UA, signatures and LibreOffice conversion
Requires
- php: >=8.5
- ext-ctype: *
- ext-mbstring: *
- ext-uri: *
- ext-zlib: *
- phil/url: ^0.1@dev
Requires (Dev)
- ext-openssl: *
- phpunit/phpunit: ^13.4
- symfony/http-client: ^8.2
- symfony/http-client-contracts: ^3.6
Suggests
- symfony/http-client: For the Gotenberg client, which sends its requests through Symfony HttpClient
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-13 22:45:36 UTC
README
PDF documents from PHP, with real fonts, and nothing to install beside it.
phil/paged-media writes PDF files and works on them. Its native writer places text, lines, rectangles, images and
fillable form fields on pages of any size, in the standard Helvetica fonts or in a TrueType font it embeds, reduced to
the glyphs the document prints, so a receipt, a ticket or a printable sheet can be written in any script; its printed
templates draw name badges, labels, gift and download cards, calendars and large-print editions from data. Its
Gotenberg client sends PDFs to a
Gotenberg 9 service to merge, split, stamp, fill, redact, tag, encrypt, sign and timestamp
them, to make them PDF/A, PDF/UA or Factur-X and check that they are, to read their information, and to convert office
documents with LibreOffice. It does not lay out HTML or CSS: that is the job of a paged-media engine, for which its
PageRule writes the @page rule of a printed page, with its bleed and printer's marks.
It is for any PHP code that must produce a PDF: invoices and receipts, tickets, labels, certificates and reports with
the native writer alone, with no browser, binary or service; archived, accessible, signed or e-invoicing documents, and
documents written in an office suite, through Gotenberg. phil/commerce-bundle prints its receipts with it.
It is a plain PHP library. The native writer requires only ext-ctype, ext-mbstring and
ext-zlib, and its output is deterministic: the same calls give the same bytes, which makes documents comparable and
testable. The Gotenberg client speaks through Symfony HttpClient, a suggested dependency, and checks every request
before it is sent — a stage's options, the order of a list of stages, the addresses Gotenberg fetches, read by
phil/url — so a request Gotenberg would refuse is never sent.
Status
| Part | State |
|---|---|
Document: pages, text, right-aligned text, lines, rectangles, images, compressed streams, information dictionary |
Implemented and tested |
StandardFont: Helvetica and Helvetica Bold in Windows-1252, with their widths |
Implemented and tested |
| Form fields: text boxes, check boxes, radio groups, combo and list boxes, with their appearances; kept fillable or flattened | Implemented and tested |
| Printed templates: name badges, address, product and shelf labels, gift cards, download cards, calendars, large print, on label sheets | Implemented and tested |
TrueTypeFont: reading, measuring, embedding reduced to the glyphs used, the font's embedding licence respected |
Implemented and tested |
Image: JPEG kept as it is; PNG kept compressed, or split into colour and alpha |
Implemented and tested |
| PDF/A-2u for archiving: XMP metadata, sRGB output intent, file identifier, and refusal of what the standard forbids | Implemented and tested |
| Gotenberg 9 client: the 28 PDF stages in a list or alone, typed and checked, and Gotenberg's order rules | Implemented and tested, and run against Gotenberg 9.1.0 |
| Gotenberg: PDF/A, PDF/UA and Factur-X remediation with its residual counts, and validation | Implemented and tested, and run against Gotenberg 9.1.0 |
| Gotenberg: signatures from certificate sources (files, text, HashiCorp Vault or OpenBao) and RFC 3161 timestamps | Implemented and tested, and run against Gotenberg 9.1.0 |
| Gotenberg: the LibreOffice conversion with its native options, the read routes, webhooks, downloads, basic authentication, failures by status | Implemented and tested, and run against Gotenberg 9.1.0 |
PageRule: the @page rule of an HTML and CSS engine, with size, margins, bleed and printer's marks |
Implemented and tested |
HTML and CSS engines: WeasyPrint, in phil/paged-media-weasyprint, behind the PrintEngine port of phil/paged-media-bundle |
Implemented and tested in its package |
| Other engines behind the same port, a page limit for an engine, formulas drawn as SVG for an engine without MathML | Not started |
Documentation
| Document | What it covers |
|---|---|
| Installation | Requirements and installing. |
| Documents | Document, PageSize, Color and Metadata: pages, text, shapes, determinism. |
| Archiving | PDF/A-2u: what the document adds, and what it refuses. |
| Forms | Fillable fields, their appearances and fonts, PDF/A and flattening. |
| Printed templates | Badges, labels, gift and download cards, calendars and large print, from data. |
| HTML and CSS engines | PageRule for a paged-media engine, and what the engines add. |
| Fonts | StandardFont and TrueTypeFont: measuring, wrapping, embedding and licences. |
| Images | Image: the JPEG and PNG files it reads, and how they are written. |
| Gotenberg | The client: files, results, webhooks, downloads, credentials and failures. |
| Stages | Every stage, page selections, and the order rules of a list. |
| Conformance | PDF/A, PDF/UA and Factur-X: remediation and validation. |
| Signing | Certificates and their sources, signatures, timestamps. |
| LibreOffice | Converting office documents, and their native options. |
| Reading a PDF | Information, metadata, bookmarks, attachments and form fields. |
| Exceptions | PagedMediaException, the writer's exceptions and the client's. |
| Extension points | The ports, arguments and data an application chooses or replaces. |
| Testing | The library's tests and mutation bar, and how to test code that writes PDFs. |
| Reference | Every type of the library, with its role. |
Quick start
composer require phil/paged-media
use Phil\PagedMedia\Font\StandardFont; use Phil\PagedMedia\Font\TrueTypeFont; use Phil\PagedMedia\Pdf\Color; use Phil\PagedMedia\Pdf\Document; use Phil\PagedMedia\Pdf\Metadata; use Phil\PagedMedia\Pdf\PageSize; $font = TrueTypeFont::fromFile('/path/to/NotoSans-Regular.ttf'); $pdf = new Document(PageSize::a5(), new Metadata(title: 'Ticket')); $pdf->addPage(); $pdf->text(36, 540, 'Admit one', 18, StandardFont::HelveticaBold); $pdf->text(36, 515, 'Élise Tremblay — 東京', 12, $font); $pdf->line(36, 505, 384, 505, 0.5, Color::gray(128)); $pdf->textRight(384, 480, '12,50 $', 12, $font); file_put_contents('ticket.pdf', $pdf->render());
Through Gotenberg, the same ticket archived and signed:
use Phil\PagedMedia\Gotenberg\Files; use Phil\PagedMedia\Gotenberg\Gotenberg; use Phil\PagedMedia\Gotenberg\InputFile; use Phil\PagedMedia\Gotenberg\Pipeline; use Phil\PagedMedia\Gotenberg\Profile; use Phil\PagedMedia\Gotenberg\Signing\FileCertificateSource; use Phil\PagedMedia\Gotenberg\Stage\Remediate; use Phil\PagedMedia\Gotenberg\Stage\Sign; use Symfony\Component\HttpClient\HttpClient; $gotenberg = new Gotenberg(HttpClient::create(), 'http://gotenberg:3000'); $result = $gotenberg->pipe( Files::of(InputFile::fromDocument('ticket.pdf', $pdf)), new Pipeline(new Remediate(Profile::PdfA3b), Sign::with(new FileCertificateSource('/run/secrets/signer.p12', null, $password))), ); $result->saveTo('ticket.pdf'); // $result->residual === 0: it conforms
Requirements
PHP 8.5 or later with ext-ctype, ext-mbstring, ext-uri and ext-zlib, and phil/url; Symfony HttpClient and a
Gotenberg 9 service for the Gotenberg client. See Installation.
Contributing
Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.
License
MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.