Создание многостраничного документа с автоматическими разрывами страниц
Краткий обзор
Заголовок раздела «Краткий обзор»Создайте документ на несколько страниц. Добавляйте контент по мере необходимости. Когда setAutoPageBreak() включён, движок макета начинает новую страницу, как только курсор достигает нижнего поля. После save() считайте итоговое число страниц с помощью getNumPages(). Этот рецепт следует examples/05-multi-page.php.
Во время save() движок записывает разметку каждой страницы в поток содержимого. ISO 32000-2 §7.7.3.3 определяет Contents страницы как один поток или массив потоков, соединённых по порядку. Поэтому многостраничный вывод представляет собой последовательность объектов страниц, а не единый буфер.
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Дополнительное расширение не требуется. Этот рецепт работает в матрице бэкпортов PHP 8.1–8.4. getNumPages() и setAutoPageBreak() остаются стабильными начиная с 1.0.0.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Документ NextPDF — это дерево страниц. По мере добавления контента внутренний курсор (getY()) перемещается вниз по странице. Когда автоматические разрывы страниц включены, движок перед каждым блоком контента проверяет оставшееся вертикальное пространство. Если блок не помещается выше нижнего поля, движок завершает текущую страницу и сам вызывает addPage(). Нижнее поле, которое вы передаёте в setAutoPageBreak(), служит порогом срабатывания.
Атрибуты уровня страницы, такие как media box, наследуются. ISO 32000-2 §7.7.3.4 устанавливает, что атрибут, опущенный в объекте страницы, разрешается из родительского узла дерева страниц. NextPDF задаёт единый размер страницы для всего документа, поэтому каждая созданная страница использует одинаковую геометрию, и вам не нужно указывать её для каждой страницы.
Поверхность API
Заголовок раздела «Поверхность API»Поверхность API генерируется из PHPDoc. Этот рецепт опирается на следующие методы:
Document::createStandalone(): self— создаёт изолированный документ.setAutoPageBreak(bool $enabled, float $margin = 20): static— включает автоматические разрывы страниц.$margin— порог нижнего поля в миллиметрах.addPage(?PageSize $size = null, Orientation $orientation = Orientation::Portrait): static— начинает первую страницу и любую явно заданную страницу.multiCell(...): static/cell(...): static— выводят перетекающие или фиксированные текстовые блоки. Проверка разрыва страницы измеряет именно эти блоки.getNumPages(): int— число страниц после построения макета.
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setAutoPageBreak(true, margin: 25);$doc->addPage();
$doc->setFont('helvetica', '', 11);for ($i = 1; $i <= 60; $i++) { $doc->multiCell(0, 7, "Line {$i}: content flows until the page is full, " . 'then the engine starts a new page automatically.');}
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf');echo 'Pages: ' . $doc->getNumPages() . "\n";Пример кода — рабочая версия
Заголовок раздела «Пример кода — рабочая версия»Это полный пример, готовый для запуска в тестовой обвязке. Он учитывает NEXTPDF_COOKBOOK_OUTPUT, которую задаёт тестовая обвязка, поэтому не выводите PDF в STDOUT. Пример не добавляет собственной энтропии. При запуске из тестовой обвязки DeterministicMode фиксирует часы, /ID и брендинг.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Multi-Page Document');
// Enable automatic page breaks. The 25 mm bottom margin is the trigger:// when the cursor would cross it, the engine flushes the page and adds// a new one before the next block is drawn.$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);
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++) { $text = "Paragraph {$para} of Chapter {$chapter}. " . 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' . 'Sed do eiusmod tempor incididunt ut labore et dolore magna ' . 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation ' . 'ullamco laboris nisi ut aliquip ex ea commodo consequat.'; $doc->multiCell(0, 7, $text); $doc->ln(3); } $doc->ln(5);}
// The harness sets NEXTPDF_COOKBOOK_OUTPUT; honour it. STDOUT stays free// for progress text only.$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf';$doc->save($out);
echo 'Created multi-page.pdf with ' . $doc->getNumPages() . " pages\n";Краевые случаи и подводные камни
Заголовок раздела «Краевые случаи и подводные камни»- Автоматический разрыв страниц отключён. При
setAutoPageBreak(false, …)контент за нижним полем обрезается по странице, а не перетекает, и документ остаётся на одной странице. Включите его для перетекающего контента. - Отдельный блок выше страницы. Движок внутренне разбивает
multiCell, если его текст превышает печатаемую высоту. Но отдельный неделимый блок выше полезной области, например высокое изображение, размещается один раз и выходит за её пределы. Разбейте его самостоятельно. - Первый вызов
addPage()по-прежнему обязателен.cell()при необходимости вызываетaddPage(), когда страницы ещё нет. Тем не менее вызывайтеaddPage()явно, чтобы размер и ориентация первой страницы оставались детерминированными. - Единицы измерения поля. Поле для
setAutoPageBreak()задаётся в миллиметрах в системе единиц по умолчанию, а не в пунктах.
Производительность
Заголовок раздела «Производительность»getNumPages() работает за O(1). Он считывает счётчик и не запускает построение макета заново. Потребление памяти растёт пропорционально сохраняемому контенту, а не числу страниц. Движок сбрасывает завершённые страницы в выходной буфер по мере готовности — это однопроходная потоковая модель (ADR-001). Бюджета 2000 ms / 64 MB достаточно для документов с несколькими сотнями страниц текста на эталонном узле.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»Этот рецепт записывает только тот текст, который предоставляет ваш код. Он не выполняет разбор входных данных, сетевые обращения или десериализацию. Любой текст из внешних источников считайте недоверенным и ограничивайте его длину перед отрисовкой. Движок не накладывает за вас ограничение на размер контента на уровне приложения.
Соответствие
Заголовок раздела «Соответствие»| Утверждение | Спецификация | Раздел | идентификатор ссылки (reference_id) |
|---|---|---|---|
Элемент Contents страницы — это один поток или упорядоченный соединённый массив потоков. | ISO 32000-2 | §7.7.3.3 | |
| Наследуемый атрибут страницы, опущенный в объекте страницы, разрешается из родительского узла дерева страниц. | ISO 32000-2 | §7.7.3.4 | |
Элемент /ID в трейлере — это идентификатор файла из двух байтовых строк (обязателен в PDF 2.0). | ISO 32000-2 | §7.5.5 |
Профиль воспроизводимости — структурный (почему не побитовый). Каждый сохранённый документ содержит в трейлере /ID, две байтовые строки которого являются идентификатором файла (ISO 32000-2 §7.5.5, выше). Второй элемент нестабилен между запусками, поэтому сырые байты различаются от запуска к запуску даже при идентичном контенте. Тестовая обвязка сравнивает нормализованную с помощью qpdf структуру, из которой удаляются /ID, /CreationDate и /ModDate. Этот рецепт описывает, как NextPDF формирует структуру. Он не делает всеобъемлющего заявления о соответствии ISO 32000-2.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»Неприменимо. Компоновка многостраничных документов с автоматическими разрывами страниц — возможность Core без ограничения Premium.