01 — Build

Make a PDF from nothing.

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.

01

Simple generation

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 →
hello.php
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');
02

HTML‑to‑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.

colspan, rowspan, and header rows that repeat across page breaks
Borders and background colors on any element, not just cells

Known limits: no border-collapse, no nested tables, and less common CSS may not be covered.

HTML parsing in the docs →
invoice.php
use Pop\Pdf\Pdf;

// however you already render templates
$html = '<h1>Invoice 1234</h1>';

$document = Pdf::importFromHtml($html);

Pdf::writeToFile($document, 'invoice.pdf');
03

Advanced generation

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.

Letter, Legal, Tabloid, A0–A9, B0–B10, envelopes, or any custom size
Page management as methods — copy, reorder, delete
Text alignment and wrapping inside a bounding box
Documents and pages in the docs →
report.php
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');
04

Simple & complex fonts

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 →
fonts.php
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');
05

Annotations & links

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 →
links.php
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');
06

Drawing paths

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 →
paths.php
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');
07

Forms

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 →
form.php
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');
08

Encryption & permissions

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 →
protect.php
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');