A one-page document from scratch is a few lines. Or pass it HTML and it does the work for you. Either way you get back the same document object — keep adding to it, write it to disk, or send it to the browser.
No template language, no config, no rendering engine. Make a document, add a font, add a page, put text on it.
To send it to the browser instead of disk, swap the last line for Pdf::outputToHttp().
Simple generation in the docs →use Pop\Pdf\Pdf; use Pop\Pdf\Document; use Pop\Pdf\Document\Font; use Pop\Pdf\Document\Page; $document = new Document(); $document->addFont(Font::ARIAL); $page = $document->createPage(Page::LETTER); $page->addText( 'Hello World', Font::ARIAL, x: 50, y: 742 ); Pdf::writeToFile($document, 'hello.pdf');
Render HTML and CSS into pages without a headless browser. Render your template however you already do, and pass the string straight in.
Got a file on disk with linked stylesheets and images instead? Pdf::importFromHtmlFile() takes the path.
Known limits: no border-collapse, no nested tables, and less common CSS may not be covered.
HTML parsing in the docs →use Pop\Pdf\Pdf; // however you already render templates $html = '<h1>Invoice 1234</h1>'; $document = Pdf::importFromHtml($html); Pdf::writeToFile($document, 'invoice.pdf');
Named styles so a type change is one edit. Images that resize on the way in. Compression on a flag. And an origin you can move to the top left, if PDF’s bottom-left math isn’t how you think.
use Pop\Pdf\Pdf; use Pop\Pdf\Document; use Pop\Pdf\Document\Font; use Pop\Pdf\Document\Page; use Pop\Pdf\Document\Page\Image; $document = new Document(); $document->addFont(Font::ARIAL) ->setCompression(true) ->setOrigin(Document::ORIGIN_TOP_LEFT); // one style, referenced everywhere $document->createStyle('body', Font::ARIAL, 12); $page = $document->createPage(Page::LETTER); $page->addText( 'Q3 Results', 'body', x: 50, y: 50 ); $image = Image::createImageFromFile('chart.jpg'); $image->resizeToWidth(320); $page->addImage($image, x: 50, y: 300); Pdf::writeToFile($document, 'report.pdf');
The standard 14 fonts are built in and ship with every reader. Embed TrueType, OpenType or Type1 and they compile as composite CID fonts with a /ToUnicode CMap — so non-Latin text renders correctly and still copies and extracts.
A missing glyph throws at compile time, naming the font and the character — not mojibake in production.
Fonts in the docs →use Pop\Pdf\Pdf; use Pop\Pdf\Document; use Pop\Pdf\Document\Font; use Pop\Pdf\Document\Page; use Pop\Pdf\Document\Page\Text; $document = new Document(); $page = $document->createPage(Page::LETTER); // standard — no font file needed $document->addFont(Font::HELVETICA_BOLD); // embedded — any font beyond the standard 14 $font = new Font('/path/to/DejaVuSans.ttf'); $document->embedFont($font); // embedded fonts support non-Latin characters $page->addText( new Text('123 ПРИВІТ:', size: 36), $font->getName(), x: 50, y: 600 ); // measure before you place $width = $font->getStringWidth('Hello', 12); Pdf::writeToFile($document, 'fonts.pdf');
Link out to a URL, or jump to a page — coordinate and zoom level inside the same document — which is what a generated table of contents needs.
Annotations in the docs →use Pop\Pdf\Pdf; use Pop\Pdf\Document; use Pop\Pdf\Document\Font; use Pop\Pdf\Document\Page; use Pop\Pdf\Document\Page\Annotation\Link; use Pop\Pdf\Document\Page\Annotation\Url; $document = new Document(); $font = new Font(Font::ARIAL); $document->addFont($font); $page = $document->createPage(Page::LETTER); // external — hotspot sized to the text $text = 'Example'; $page->addText( $text, Font::ARIAL, x: 50, y: 742 ); $url = new Url( $font->getStringWidth($text, 12), 15, 'https://example.com/' ); $page->addUrl($url, x: 50, y: 742); // internal: page 2, at 110% zoom $text = 'Page 2'; $page->addText( $text, Font::ARIAL, x: 50, y: 700 ); $link = new Link( $font->getStringWidth($text, 12), 15, xTarget: 10, yTarget: 752 ); $link->setPageTarget(2)->setZTarget(1.1); $page->addLink($link, x: 50, y: 700); // the page that link jumps to $document->createPage(Page::LETTER); Pdf::writeToFile($document, 'links.pdf');
Vector graphics, not a bitmap pasted on top. Lines, rectangles, ellipses, polygons and more — all with fill, stroke, dash and clipping styles.
Enough to draw charts, rules, watermarks and callouts without leaving PHP.
Paths in the docs →use Pop\Pdf\Pdf; use Pop\Color\Color; use Pop\Pdf\Document; use Pop\Pdf\Document\Page; use Pop\Pdf\Document\Page\Path; $document = new Document(); $page = $document->createPage(Page::LETTER); $path = new Path(Path::FILL_STROKE); $path->setFillColor(Color::rgb(155, 20, 20)) ->setStrokeColor(Color::rgb(81, 125, 153)) ->setStroke(5) ->drawRectangle(50, 400, 320, 240) ->drawCircle(210, 520, 60); $page->addPath($path); Pdf::writeToFile($document, 'paths.pdf');
Fillable PDFs, generated. Text, multiline, single- and multi-select choice, combo, push-button and radio fields, attached to a named form on the document.
Reader support for form fields varies. Adobe’s readers are the reliable target.
Forms in the docs →use Pop\Pdf\Pdf; use Pop\Pdf\Document; use Pop\Pdf\Document\Font; use Pop\Pdf\Document\Form; use Pop\Pdf\Document\Page; use Pop\Pdf\Document\Page\Field\Text; $document = new Document(); $document->addFont(Font::ARIAL); $page = $document->createPage(Page::LETTER); $form = new Form('contact'); $document->addForm($form); $name = new Text('name'); $name->setWidth(200)->setHeight(20); $page->addText( 'Name', Font::ARIAL, x: 50, y: 725 ); $page->addField($name, 'contact', x: 50, y: 700); Pdf::writeToFile($document, 'form.pdf');
Lock the document you just built. AES‑256 by default, AES‑128 if something older has to read it, and a permissions object that switches off printing, copying, modifying, annotating, form filling or assembly — each one allowed until you say otherwise.
The user password opens the document; the owner password overrides the permissions. Leave the owner password unset and one is generated for you, so a blank password is never the way around them.
Reader support for security features varies. Permissions in particular are honored at the reader’s discretion.
Encryption in the docs →use Pop\Pdf\Pdf; use Pop\Pdf\Document; use Pop\Pdf\Document\Font; use Pop\Pdf\Document\Page; use Pop\Pdf\Document\Security; use Pop\Pdf\Document\Permissions; $document = new Document(); $document->addFont(Font::ARIAL); $page = $document->createPage(Page::LETTER); $page->addText( 'Confidential', Font::ARIAL, x: 50, y: 742 ); $permissions = (new Permissions()) ->allowPrinting(false) ->allowCopying(false); $security = new Security( userPassword: 'open-me', ownerPassword: 'admin123', permissions: $permissions ); $document->setSecurity($security); Pdf::writeToFile($document, 'protected.pdf');