Skip to content

Core / HasPages trait

The HasPages concern trait manages pages. Use it to add, move, copy, and delete pages; set margins; move the cursor; and control automatic page breaks.

Terminal window
composer require nextpdf/core:^3

trait HasPages (src/Core/Concerns/HasPages.php, since 1.2.0) is composed into Document. To add a page:

  1. Call addPage(?PageSize $size = null, Orientation $orientation = Orientation::Portrait).
  2. Optionally call setMargins(Margin $margin) or one of setLeftMargin(), setTopMargin(), setRightMargin().
  3. Write content. The cursor resets to the top-left margin on each new page.

When you call addPage(), it flushes the current page if one is open, applies the requested size and orientation, resets the cursor to the top-left margin, and renders the configured header. The page-count cap is 10,000 pages and is fixed by an internal Document constant (private const MAX_PAGES, not part of the public API). A call past the limit throws PageLayoutException. When $size is omitted, the current page size carries forward. Orientation is applied by calling portrait() or landscape() on the size.

The coordinate model uses user units, with Y increasing downward from the page top. setX(), setY(), and setXY() move the cursor. getX(), getY(), getPageWidth(), getPageHeight(), and getMargins() read current state. setAutoPageBreak(bool $enabled, float $margin = 20) enables a break when content would overflow past the bottom-margin threshold. The next content call then adds a page automatically.

Page-collection methods — movePage(int $from, int $to), copyPage(int $index), deletePage(int $index) — reorder pages in the document. startPageGroup() begins a new page-numbering group. getGroupPageNo() returns the group-relative number. getPage() returns the zero-based current index (-1 when no page exists). getNumPages() returns the total, including the active unflushed page.

The writer emits a page tree with consistent Kids and Count values (ISO 32000-2:2020 §7.7.3). Attributes such as Resources can be inherited from an ancestor page-tree node (§7.7.3). When tagged Portable Document Format (PDF) mode is active, addPage() closes every open marked-content scope before the page break. It reopens those scopes on the new page with fresh marked-content identifiers (MCIDs), so each begin marked-content (BDC) operator has a matching end marked-content (EMC) operator on its own page (§14.6).

SymbolKindStabilitySince
addPage(?PageSize, Orientation): staticmethodstable1.2.0
setMargins(Margin): staticmethodstable1.2.0
setLeftMargin/setTopMargin/setRightMargin(float): staticmethodstable1.2.0
setAutoPageBreak(bool, float): staticmethodstable1.2.0
setX/setY/setXY(float[, float]): staticmethodstable1.2.0
getX/getY/getPageWidth/getPageHeight(): floatmethodstable1.2.0
getPage(): int / getNumPages(): intmethodstable1.2.0
movePage/copyPage/deletePage(int): staticmethodstable1.2.0
startPageGroup(): static / getGroupPageNo(): intmethodstable1.2.0
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Contracts\Orientation;
use NextPDF\Core\Document;
use NextPDF\ValueObjects\{Margin, PageSize};
$doc = Document::createStandalone();
$doc->setTitle('Page Setup Examples');
$doc->setLanguage('en');
// Page 1: A4 portrait with custom margins
$doc->addPage(PageSize::A4(), Orientation::Portrait);
$doc->setMargins(new Margin(20.0, 15.0, 20.0, 15.0));
$doc->setFont('helvetica', 'B', 16);
$doc->cell(0, 12, 'Page 1: A4 Portrait', newLine: true);
// Page 2: Letter landscape
$doc->addPage(PageSize::Letter(), Orientation::Landscape);
$doc->setFont('helvetica', 'B', 16);
$doc->cell(0, 12, 'Page 2: Letter Landscape', newLine: true);
$doc->save(__DIR__ . '/output/03-page-setup.pdf');

Source: examples/03-page-setup.php.

setAutoPageBreak() lets content flow across pages without manual page breaks.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Multi-Page Document');
$doc->setAutoPageBreak(true, margin: 25);
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Multi-Page Document Example', newLine: true);
$doc->ln(5);
$doc->setFont('helvetica', '', 11);
for ($chapter = 1; $chapter <= 3; $chapter++) {
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, "Chapter {$chapter}: Lorem Ipsum", newLine: true);
$doc->setFont('helvetica', '', 11);
for ($para = 1; $para <= 5; $para++) {
$doc->multiCell(0, 7, "Paragraph {$para} of Chapter {$chapter}. "
. 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.');
}
}
$doc->save(__DIR__ . '/output/05-multi-page.pdf');

Source: adapted from examples/05-multi-page.php.

  • getNumPages() includes the active page that is not flushed yet; it is not the count of completed pages alone.
  • getPage() returns -1 before the first addPage().
  • Calling addPage() past the internal 10,000-page cap throws PageLayoutException.
  • Y increases downward in user coordinates. The engine converts to the native PDF bottom-left origin internally. Do not pre-flip Y yourself.
  • The single-margin setters rebuild the full Margin value object, preserving the other three sides. There is no partial mutation.
  • In tagged PDF mode, addPage() reopens active tag scopes with fresh MCIDs. Do not assume an MCID survives a page break.

addPage() flushes one page buffer and starts another in O(page content size). The engine holds one active page plus the cursor, not a retained page list with full content (ADR-001). movePage()/copyPage()/deletePage() operate on the flushed page collection. Budget: 1500 ms / 64 MB for the canonical quick start.

Page operations consume no external input other than the integer indices the caller supplies. PageManager validates out-of-range indices. This trait has no untrusted parsing path.

ClaimSourceClausereference_id
The page tree links children through Kids arraysISO 32000-2:2020§7.7.3
Count is kept consistent with the Kids arrayISO 32000-2:2020§7.7.3
Resources may be inherited from an ancestor page-tree nodeISO 32000-2:2020§7.7.3

Clauses are paraphrased; no normative text is reproduced.