Макет: верхние и нижние колонтитулы, колонки, буклет, диспетчер страниц
Модуль Layout объединяет движки компоновки страниц за фасадом Document: верхние и нижние колонтитулы, многоколоночный макет, спуск полос буклета внакидку и структурные операции со страницами. Он небольшой и стабильный: шесть классов, все с @since 1.0.0.
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Layout (src/Layout/, шесть классов, @since 1.0.0) — это слой движков под трейтом HasLayout. Ваше приложение вызывает методы фасада на Document; трейт передаёт каждый вызов соответствующему движку. Манифест помечает модуль уровнем риска standard и уровнем стабильности internal, а единственный модуль, который от него зависит, — Core. Используйте его через фасад, а не создавайте классы напрямую.
HeaderFooter отрисовывает повторяющиеся верхний и нижний колонтитулы. Он хранит состояние заголовка, описания, логотипа, шрифта, отступа и цвета для каждой области. По запросу через renderHeader() и renderFooter() он генерирует операторы потока содержимого Portable Document Format (PDF). Нижний колонтитул по умолчанию печатает строку "page / total" с выравниванием по правому краю. setHeaderCallback() и setFooterCallback() заменяют стандартный макет замыканием из вызывающего кода. getHeaderContentHeight() сообщает, сколько вертикального пространства занимает верхний колонтитул, чтобы тело страницы могло начинаться ниже него. Когда документ находится в режиме тегированного PDF, HasPages подавляет автоматический верхний колонтитул на более раннем этапе конвейера, потому что его содержимое находится вне дерева структуры.
ColumnLayout управляет многоколоночным потоком. setEqualColumns(int $count, float $totalWidth, float $gap = 5) делит доступную ширину на равные колонки. setColumnsArray() принимает явные позиции и ширины ColumnDefinition. Используйте selectColumn(), чтобы выбрать колонку, или nextColumn(), чтобы перейти к следующей. getCurrentColumnX() / getCurrentColumnWidth() возвращают геометрию активной колонки. Некорректное количество колонок, отрицательный зазор или неположительная вычисленная ширина колонки вызывают PageLayoutException.
BookletLayout переупорядочивает страницы для переплёта внакидку (со сгибом по центру). reorderPages() дополняет список страниц до числа, кратного четырём, поскольку на листе буклета есть четыре позиции страниц, а затем располагает страницы от внешних к внутренним, чтобы сложенные и сшитые скобами листы читались по порядку. getMarginAdjustments() возвращает внутренний (у корешка) и внешний (у края) отступы для каждой стороны в заданной позиции. getSheetCount() сообщает, сколько двусторонних листов требуется для заданного числа страниц. Переупорядочивание изменяет только размещение содержимого. Базовая последовательность страниц PDF остаётся линейной и согласуется с деревом страниц, которое определяет порядок страниц в документе (ISO 32000-2 §7.7).
PageManager предоставляет структурные операции со страницами независимо от отрисовки содержимого. movePage(), copyPage() и deletePage() работают с массивом PageData по ссылке. Области страницы (addPageRegion(), isInRegion(), getRegionOffset()) задают зоны, в которые нельзя писать. Группы страниц (startPageGroup(), getGroupPageNo()) поддерживают нумерацию страниц по разделам. PageRegion и ColumnDefinition — это два объекта-значения, которые используют эти движки. Модуль Writer сериализует получившиеся страницы в дерево страниц, где запись Kids — это массив косвенных ссылок на непосредственные дочерние элементы узла дерева страниц (ISO 32000-2 §7.7.3.2).
Поверхность API
Заголовок раздела «Поверхность API»| Символ | Вид | Стабильность | Начиная с |
|---|---|---|---|
HeaderFooter::setHeaderData(string, string, string, float): self | метод | стабильно | 1.0.0 |
HeaderFooter::setHeaderFont(string, float): self / setHeaderMargin(float): self | метод | стабильно | 1.0.0 |
HeaderFooter::setFooterFont(string, float): self / setFooterMargin(float): self | метод | стабильно | 1.0.0 |
HeaderFooter::setHeaderCallback(Closure): self / setFooterCallback(Closure): self | метод | стабильно | 1.0.0 |
HeaderFooter::getHeaderContentHeight(): float | метод | стабильно | 1.0.0 |
HeaderFooter::renderHeader(float, float, float, float, int, int): string | метод | стабильно | 1.0.0 |
HeaderFooter::renderFooter(float, float, float, float, int, int, string): string | метод | стабильно | 1.0.0 |
ColumnLayout::setEqualColumns(int, float, float): self | метод | стабильно | 1.0.0 |
ColumnLayout::setColumnsArray(array): self / resetColumns(): self | метод | стабильно | 1.0.0 |
ColumnLayout::selectColumn(int): self / nextColumn(): bool | метод | стабильно | 1.0.0 |
ColumnLayout::getCurrentColumnX(float): float / getCurrentColumnWidth(float): float | метод | стабильно | 1.0.0 |
BookletLayout::setBooklet(bool, float, float): void | метод | стабильно | 1.0.0 |
BookletLayout::reorderPages(array): array | метод | стабильно | 1.0.0 |
BookletLayout::getMarginAdjustments(int): array{left: float, right: float} | метод | стабильно | 1.0.0 |
BookletLayout::getSheetCount(int): int | метод | стабильно | 1.0.0 |
PageManager::movePage(int, int, array): void / copyPage(int, array): void / deletePage(int, array): void | метод | стабильно | 1.0.0 |
PageManager::addPageRegion(float, float, float, float): void / isInRegion(float, float): bool | метод | стабильно | 1.0.0 |
PageManager::getRegionOffset(float, float, float, float): float | метод | стабильно | 1.0.0 |
PageManager::startPageGroup(): void / getGroupPageNo(int): int | метод | стабильно | 1.0.0 |
PageRegion / ColumnDefinition | объект-значение | стабильно | 1.0.0 |
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Header and Footer');
$doc->setHeaderData( title: 'NextPDF Example', description: 'Header and Footer Demonstration',);$doc->setHeaderFont('helvetica', 10);$doc->setHeaderMargin(5);$doc->setFooterFont('helvetica', 8);$doc->setFooterMargin(10);
$doc->addPage();$doc->setFont('helvetica', 'B', 16);$doc->cell(0, 12, 'Document with Header and Footer', newLine: true);
$doc->save(__DIR__ . '/output/13-header-footer.pdf');Источник: examples/13-header-footer.php. Верхний колонтитул отрисовывается при каждом вызове addPage(); нижний колонтитул отрисовывается при сбросе страницы.
Пример кода — рабочая версия
Заголовок раздела «Пример кода — рабочая версия»Обратный вызов нижнего колонтитула управляет текстом номера страницы, а движок колонок формирует двухколоночное тело. К обоим движкам вы обращаетесь через фасад.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Two-Column Report');
$doc->setFooterCallback(static function (Document $d): void { $d->setFont('helvetica', '', 8); $d->text(180.0, 285.0, 'Page ' . ($d->getPage() + 1));});
$doc->addPage();$doc->setEqualColumns(2, gap: 8);$doc->selectColumn(0);$doc->setFont('helvetica', '', 10);$doc->multiCell(0, 6, 'Left column flows here.');$doc->selectColumn(1);$doc->multiCell(0, 6, 'Right column flows here.');
$doc->save(__DIR__ . '/output/two-column-report.pdf');Источник: шаблон из examples/13-header-footer.php.
Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»setEqualColumns()отклоняет количество колонок меньше 1, отрицательный зазор и любой макет, у которого вычисленная ширина колонки неположительна. Он вызываетPageLayoutExceptionвместо того, чтобы возвращать деградировавший макет.selectColumn()игнорирует индекс вне диапазона и сохраняет текущую колонку; для недопустимого индекса он никогда не выбрасывает исключение.nextColumn()возвращаетfalse, если текущая колонка уже последняя.BookletLayout::reorderPages()дополняет до числа, кратного четырём, пустыми страницами, клонированными по размерам последней страницы. Пустой список страниц возвращает пустой массив. Переупорядочивание затрагивает только размещение; индексыmovePage()по-прежнему ссылаются на логический порядок.PageManager::movePage(),copyPage()иdeletePage()молча ничего не делают для индекса вне диапазона; они проверяют его черезisset()и возвращаются, не изменяя массив. Проверяйте индекс самостоятельно, если отсутствие страницы для вызывающего кода является ошибкой.getHeaderContentHeight()возвращает0.0, когда верхний колонтитул отключён или не имеет ни заголовка, ни описания. Тогда тело страницы начинается от верхнего отступа.- В режиме тегированного PDF автоматический верхний колонтитул подавляется выше по конвейеру. Для доступных документов создавайте макет с учётом структуры.
Производительность
Заголовок раздела «Производительность»Отрисовка верхнего и нижнего колонтитулов добавляет операторы в буфер активной страницы с O(объём оформления); затраты растут вместе с записываемыми заголовком, описанием и разделителем, а не с размером документа. Расчёты колонок — O(1) на вызов. BookletLayout::reorderPages() — O(n) по числу страниц, с одним проходом для дополнения; цикл спуска проходит каждую добавленную позицию один раз. Проверки областей PageManager — O(областей) на точку, а операции со страницами — O(n) при вставке в массив. Эти движки не хранят постраничное состояние всего документа, поэтому не увеличивают рост памяти на длинных документах. Доминирующая статья затрат памяти — накопленный поток содержимого, который рассматривается в концепции потоковой передачи и памяти. Задержка конвейера HyperText Markup Language (HTML) и контроль бюджета памяти описаны в PERFORMANCE-BUDGETS; они относятся к пути отрисовки HTML и не контролируют эти движки макета напрямую. performance_budget в 1500 мс / 64 МБ — это ориентир для быстрого старта, а не контракт на отдельный вызов.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»Эти движки принимают строки и путь к логотипу, переданные вызывающим кодом. Путь к логотипу проходит через движок изображений, который проверяет файл перед встраиванием. renderHeader() и renderFooter() экранируют текст заголовка, описания и номера страницы через централизованный экранировщик строк PDF перед попаданием в поток содержимого, поэтому текст вызывающего кода не может выйти за пределы грамматики литеральных строк. Обратный вызов верхнего или нижнего колонтитула выполняет код вызывающей стороны с тем же уровнем доверия, что и остальная часть документа; любые внешние данные, которые он читает, обрабатывайте соответственно. Операции со страницами PageManager перемещают ссылки на существующие PageData; они не разбирают недоверенные байты.
Соответствие стандартам
Заголовок раздела «Соответствие стандартам»| Утверждение | Стандарт | Пункт | Подтверждение |
|---|---|---|---|
| Переупорядочивание страниц для вывода буклета изменяет только размещение; дерево страниц по-прежнему определяет линейный порядок страниц в документе. | ISO 32000-2 | §7.7 | |
Запись Kids сериализованного узла дерева страниц — это массив косвенных ссылок на непосредственные дочерние элементы этого узла. | ISO 32000-2 | §7.7.3.2 |
Таблица пересказывает каждый пункт своими словами и сохраняет закреплённые термины глоссария; она не воспроизводит нормативный текст.
См. также
Заголовок раздела «См. также»- трейт Core/HasLayout — часть фасада, которая компонует движки макета.
- трейт Core/HasPages — размер страницы и отступы, используемые при вычислении колонок.
- Writer — эмиттер объектов PDF и дерева страниц, который сериализует размещённые страницы.
- Потоковая передача и память — почему движки оформления не хранят постраничное состояние и как работает путь отрисовки, ограниченный памятью.
- HTML / Ограничения потоковой передачи (ADR-001) — обоснование области применения однопроходной потоковой передачи.