Pop PDF
Build

Creating Documents

A Document is the root object that all the other pieces of Pop PDF assembles into — pages, fonts, styles and forms.

Creating a Document#

The constructor takes an optional first page, optional metadata and an optional style or array of styles, and every argument can be left off:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$document = new Document(new Page(Page::LETTER));

An empty new Document() holds no pages at all — Adding Pages covers every way to give it one. Leaving the metadata argument off still creates a default Metadata instance, so getMetadata() always returns an object rather than null. See Metadata for the second argument and Adding Text for the third.

Adding Pages#

addPage() takes a Page object and appends it, returning the document so calls chain:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$document->addPage(new Page(Page::LETTER));
$document->addPage(new Page(Page::A4));

Sizes can differ per page — see Pages & Coordinates.

addPages() takes an array of Page objects and adds every one of them in order, in a single call:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$document->addPages([
    new Page(Page::LETTER),
    new Page(Page::LETTER),
    new Page(Page::LEGAL),
]);

createPage() builds the Page for you — pass it the same size argument a Page constructor takes — adds it to the document, and returns the new page rather than the document, so it's the shortcut when the next thing you do is add content to that page:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$page = $document->createPage(Page::LETTER);

All three count toward getNumberOfPages() the same way, and pages keep the order they were added in until orderPages() changes it. createPage() is the odd one out among the three, returning the page itself rather than the document, since it hands back the object you're about to draw on:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Page\Text;
use Pop\Pdf\Document\Font;

$document = new Document();
$document->addFont(Font::ARIAL);
$page = $document->createPage(Page::LETTER);
$page->addText(new Text('Invoice #1042', size: 14), 'Arial', x: 100, y: 700);

Copying, Reordering and Deleting#

copyPage() clones an existing page, by its 1-indexed position, and appends the clone at the end of the document. The clone is independent from the moment it's made — changes to the copy never touch the original:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$document->addPages([new Page(Page::LETTER), new Page(Page::LEGAL)]);

$copy = $document->copyPage(1);
echo $document->getNumberOfPages(); // 3

The clone carries every image, text run, path and field the original has. Pass false as the second argument for an identical size and index with none of it:

PHP
$blank = $document->copyPage(2, preserveContent: false);

A position outside the document raises Pop\Pdf\Document\Exception, naming the position:

PHP
try {
    $document->copyPage(99);
} catch (\Pop\Pdf\Document\Exception $exception) {
    echo $exception->getMessage(); // Error: That page (99) does not exist.
}

orderPages() takes an array naming, in the new order, the 1-indexed page number for each position — [3, 1, 2] puts the current page 3 first, page 1 second, page 2 last. The array must name every page exactly once; a wrong length or an unknown page number raises Pop\Pdf\Document\Exception:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$document->addPages([new Page(Page::LETTER), new Page(Page::LEGAL), new Page(Page::A4)]);

$document->orderPages([3, 1, 2]);

The two error cases have distinct messages:

PHP
try {
    $document->orderPages([3, 1]); // only 2 entries for a 3-page document
} catch (\Pop\Pdf\Document\Exception $exception) {
    echo $exception->getMessage();
    // Error: The pages array passed does not contain the same number of pages as the PDF.
}

try {
    $document->orderPages([3, 1, 9]);
} catch (\Pop\Pdf\Document\Exception $exception) {
    echo $exception->getMessage();
    // Error: The pages array passed contains a page that does not exist.
}

deletePage() removes a page by its 1-indexed position without renumbering the rest: delete page 1 from a three-page document and pages 2 and 3 keep their numbers, while getPage(1) raises Pop\Pdf\Exception until something fills it. A page added afterward takes the next number after the highest in use, not the gap — delete page 1, add one, and you get pages 2, 3 and 4:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$document->addPages([new Page(Page::LETTER), new Page(Page::LEGAL), new Page(Page::A4)]);

$document->deletePage(1);
echo $document->getNumberOfPages(); // 2

orderPages() closes that gap, since it rebuilds the page list from scratch — passing the surviving page numbers in their current order renumbers the document back to a clean, gap-free sequence:

PHP
$document->orderPages([2, 3]);
echo $document->getPage(1)->getWidth(); // the page that used to answer to number 2

deletePage() raises the same exception, worded the same way, for a position the document does not have.

The Current Page#

An empty new Document() starts with no current page at all — getCurrentPage() returns null and getNumberOfPages() returns 0 until the first page is added. From there it moves automatically: addPage(), addPages(), createPage() and copyPage() all set it to the page most recently added. setCurrentPage() moves it explicitly and getCurrentPage() reads it back:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;

$document = new Document();
$document->addPages([new Page(Page::LETTER), new Page(Page::LETTER)]);

$document->setCurrentPage(1);
echo $document->getCurrentPage();    // 1
echo $document->getNumberOfPages();  // 2

setCurrentPage() raises Pop\Pdf\Document\Exception for a page number the document doesn't have. Deleting the current page moves the pointer instead of leaving it dangling: it becomes one less than the pages remaining, or nothing once the document is empty — delete page 5 from a five-page document and the current page becomes 3.

The current page is bookkeeping for the caller only — nothing inside Pop PDF reads it back to decide what to compile.

Inspecting a Document#

Every collection a document holds — pages, fonts, styles and forms — has a has*() that reports whether any exist and a get*() that returns them.

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Font;

$document = new Document(new Page(Page::LETTER));
$document->addFont(Font::ARIAL);

$document->hasPages();   // true
$document->hasFonts();   // true
$document->hasStyles();  // false
$document->hasForms();   // false

$document->getPages();          // array of Page objects
$document->getAvailableFonts(); // ['Arial']

getPage() returns a single page by its 1-indexed position, and getFont() and getStyle() return a single member of their collection by name — all three raise an exception when the document doesn't have what was asked for. isFontAvailable()/hasFont() and isStyleAvailable()/hasStyle() are each the same check under two names:

PHP
$page = $document->getPage(1);
$arial = $document->getFont('Arial');
$document->isFontAvailable('Arial'); // true

getForm() is the one accessor here that doesn't throw — a name the document doesn't have returns null, so it's safe to call before checking hasForms():

PHP
use Pop\Pdf\Document;

$document = new Document();
$form = $document->getForm('contact'); // null, no exception

The full set of collection accessors:

  • getPages() / getPage($p) / hasPages() — every page, one page by position, or whether any exist
  • getFonts() / getFont($name) / hasFonts() / getAvailableFonts() / hasFont($name) — the document's fonts, keyed by name
  • getStyles() / getStyle($name) / hasStyles() / getAvailableStyles() / hasStyle($name) — the same shape for styles
  • getForms() / getForm($name) / hasForms() — form definitions, added with addForm()

Compression#

setCompression() turns on stream compression for everything the document writes; isCompressed() reports whether it is on. It defaults to false:

PHP
use Pop\Pdf\Document;

$document = new Document();
$document->isCompressed();     // false
$document->setCompression(true);
$document->isCompressed();     // true

The saving is real and depends on content — a five-page document built from forty lines of repeated text per page plus one filled rectangle compiled to 29,562 bytes uncompressed and 4,506 bytes with setCompression(true), a difference of 25,056 bytes, about 85% smaller.

Turn it on before the first call that compiles the document — writeToFile(), outputToHttp() or a cast to string — since compression happens at compile time, not when content is added.

PDF Version#

setVersion() and getVersion() control the version number written into the file header. It defaults to 1.7:

PHP
use Pop\Pdf\Document;

$document = new Document();
$document->getVersion();  // 1.7

$document->setVersion(1.4);
$document->getVersion();  // 1.4

The version compiles straight into the first line of the output — a document with setVersion(1.4) in force opens with %PDF-1.4 rather than %PDF-1.7. Pop PDF doesn't gate any feature behind the version number, so setting it lower disables nothing; it's only worth changing to match a target format requirement a downstream consumer specifies.

Rendering to a String#

Casting a document to a string compiles it and returns the raw PDF bytes in memory, the same bytes writeToFile() writes for that document — see Getting the Raw Bytes.

A short document assembled from several pieces on this page — two pages built directly, one copied and reordered ahead of them:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Page\Text;
use Pop\Pdf\Document\Font;

$document = new Document();
$document->addFont(Font::ARIAL);
$document->addPage(new Page(Page::LETTER));

$page = $document->createPage(Page::LETTER);
$page->addText(new Text('Page two', size: 12), 'Arial', x: 100, y: 700);

$document->copyPage(1, preserveContent: false);
$document->orderPages([3, 1, 2]);

echo $document->getNumberOfPages(); // 3
echo strlen((string)$document);     // the compiled byte count

See Also#

  • Pages & Coordinates — sizing and positioning the pages a document holds
  • Metadata — the document's title, author and the rest of the info object
  • Saving & Output — writing a document to a file or sending it to a browser