Перейти к содержимому

Руководство разработчика Artisan

Пакет Artisan решает две связанные задачи: отрисовывает HTML (Hypertext Markup Language) через Chrome и импортирует получившуюся страницу PDF (Portable Document Format) в документ NextPDF. При отладке разделяйте границы Chrome, парсера и импортёра.

Используйте это руководство при разработке интеграций рендерера, долгоживущих воркеров, диагностики парсера или тестов для nextpdf/artisan.

СлойВладелецОтветственностьНе размещать здесь
ПриложениеПриложениеАвторизация генерации HTML и выбор конфигурации рендерера.Управление процессом браузера.
Политика HTMLПриложение и пакетОтклонение небезопасного или слишком большого HTML до отрисовки.Авторизация арендатора или решения бизнес-логики.
Рендерер Chromenextpdf/artisanОтрисовка HTML в отдельный PDF, который создаёт Chrome.Универсальное восстановление PDF или произвольное редактирование PDF.
Парсер/импортёрnextpdf/artisanРазбор отрисованного PDF и импорт одной страницы как form XObject.Полная проверка соответствия PDF.
Ядро движкаnextpdf/nextpdfРазмещение импортированных объектов формы и запись итогового документа.Жизненный цикл CDP (Chrome DevTools Protocol).
ЭтапПоведениеДействие разработчика
Создание конфигурацииChromeRendererConfig задаёт исполняемый файл, тайм-аут, поведение CSS (Cascading Style Sheets), размер ввода и режим песочницы.Используйте конфигурацию для конкретного окружения вместо жёстко заданных предположений о среде выполнения.
Создание рендерераChromeHtmlRenderer владеет BrowserPool.Переиспользуйте рендерер внутри воркера, затем закрывайте его при завершении работы.
Проверка HTMLПолитика безопасности проверяет размер и оборачивает документ в CSS по умолчанию.Проверяйте авторизацию вызывающей стороны до этого этапа.
Печать через ChromeCDP отрисовывает отдельный PDF.Оставляйте внешние ресурсы заблокированными, если только проверенная политика их не разрешает.
Разбор PDFPdfReader::parse() читает данные xref, страницы, объекты, ресурсы и ревизии.Рассматривайте сбои парсера как сбои отрисовки, если диагностика не является целью.
Импорт страницыPageImporter::import() извлекает содержимое страницы, media box, ресурсы и встроенные объекты.Импортируйте страницу 0, если только рабочий процесс намеренно не выбирает другую страницу.
ПутьНазначение
app/Pdf/Renderers/*Обёртка приложения вокруг ChromeHtmlRenderer.
app/Pdf/Templates/*Отрисовка HTML-шаблонов и сопоставление DTO (data transfer object) с представлением.
app/Pdf/Policies/*Политика отрисовки по размеру HTML, ресурсам и арендатору.
tests/Pdf/Renderer/*Дымовые тесты рендерера с небольшими HTML-фикстурами.
tests/Pdf/Parser/*Фикстуры парсера для импортированного вывода Chrome.

Держите отрисовку шаблонов отдельно от отрисовки в браузере. Передавайте рендереру готовый HTML и известную ширину страницы.

<?php
use NextPDF\Artisan\ChromeHtmlRenderer;
use NextPDF\Artisan\ChromeRendererConfig;
use NextPDF\Artisan\PageImporter;
use NextPDF\Parser\PdfReader;
$renderer = new ChromeHtmlRenderer(new ChromeRendererConfig(
renderTimeout: 30,
maxHtmlSize: 1_000_000,
));
$result = $renderer->render($html, widthPt: 595.28);
$reader = new PdfReader($result->getPdfData());
$reader->parse();
$form = (new PageImporter())->import($reader);

Создавайте один рендерер для процесса-воркера или области видимости запроса. Переиспользуйте его, чтобы не тратить ресурсы на повторный запуск Chrome. Закрывайте его явно, чтобы предотвратить утечки процессов при завершении работы воркера.

<?php
final class InvoiceChromeRenderer
{
public function __construct(
private readonly ChromeHtmlRenderer $renderer,
) {}
public function renderInvoice(string $html): string
{
return $this->renderer
->render($html, widthPt: 595.28)
->getPdfData();
}
public function close(): void
{
$this->renderer->close();
}
}

Используйте программные интерфейсы (API) парсера, когда вывод Chrome не удаётся импортировать. Держите диагностику в режиме только чтения и не меняйте состояние парсера после успешного импорта.

Диагностический вопросКакой API использоватьОжидаемый сигнал
Разбирается ли файл?PdfReader::parse()Выбрасывает исключение при недопустимой структуре PDF.
Существует ли страница 0?PdfReader::getPage(0)Возвращает PdfObject.
Есть ли содержимое?PdfReader::getPageContentStream($page)Непустой поток содержимого.
Присутствуют ли ресурсы?PdfReader::getPageResources($page)Массив словаря ресурсов.
Есть ли инкрементальные ревизии?PdfReader::getRevisionCount()Счётчик больше единицы.
Какой объект дал сбой?PdfTokenizer::getOffset() и контекст исключения парсера.Байтовое смещение для минимизации фикстуры.
Точка расширенияДля чего использоватьОграничение
ChromeRendererConfig::fromArray()Сопоставление конфигурации фреймворка.Неизвестные или неверно типизированные необязательные значения заменяются значениями по умолчанию.
HtmlSecurityPolicyInterfaceПолитика HTML на уровне парсинга.Не заменяет средства контроля транспорта, процессов или авторизации.
LoggerInterfaceДиагностика отрисовки и браузера.По умолчанию не записывайте содержимое HTML в журнал.
BrowserPoolПереиспользование долгоживущего процесса Chrome.Нужно закрывать при завершении работы воркера.
PageImporterВстраивание разобранной внешней страницы.Сначала reader нужно разобрать.
Классы парсераДиагностика и импортированный вывод Chrome.Не универсальный набор инструментов для восстановления PDF.
  1. Воспроизведите фрагмент HTML в минимальном тесте отрисовки.
  2. Проверьте maxHtmlSize, CSS по умолчанию и путь к исполняемому файлу Chrome.
  3. Отрисуйте с фиксированной шириной в пунктах.
  4. Разберите возвращённые байты PDF с помощью PdfReader::parse().
  5. Импортируйте страницу 0, если только рабочий процесс намеренно не выбирает другую страницу.
  6. Добавьте тесты на фикстурах для минимального HTML, который воспроизводит каждый сбой.
  7. Закрывайте рендерер в обработчиках завершения работы воркера.
СбойГде это должно обрабатыватьсяРекомендуемая реакция
Отсутствует исполняемый файл ChromeПроверка развёртывания и путь создания рендерера.Помечайте систему как неготовую до приёма трафика отрисовки.
Слишком большой HTMLПолитика HTML.Отклоняйте до запуска Chrome.
Тайм-аут браузераГраница рендерера.Прерывайте отрисовку и записывайте имя шаблона, размер, ширину и тайм-аут.
Сбой парсераГраница импорта.Сохраняйте небольшую очищенную фикстуру для отладки, когда это разрешено политикой.
Утечка процесса браузераЖизненный цикл воркера.Закрывайте при завершении работы и перезапускайте после контролируемого числа отрисовок.
АспектПо умолчаниюКогда переопределять
Тайм-аут отрисовки30 секунд.Увеличивайте только для измеренных документов ограниченного размера.
Максимальный размер HTML5,000,000 байт.Снижайте для публичных конечных точек.
ПесочницаВключена.Отключайте только, когда этого требуют ограничения контейнера и хост изолирован.
ВысотаАвто при heightPt <= 0.Используйте фиксированную высоту для строгих контрактов макета.
Внешние ресурсыЗаблокированы политикой рендерера.Разрешайте только через проверенную политику ресурсов.
  • Тесты отрисовки покрывают репрезентативные HTML и CSS.
  • Тесты безопасности покрывают слишком большой HTML и попытки обращения к заблокированным ресурсам.
  • Тесты импорта проверяют, что возвращённый объект формы содержит содержимое, media box и ресурсы.
  • Тесты парсера покрывают таблицу перекрёстных ссылок (xref), поток xref, поток объектов и случаи некорректных фикстур.
  • Тесты воркера вызывают close() и проверяют, что не осталось процесса браузера.
  • Тесты производительности фиксируют время отрисовки по шаблону и размеру содержимого.