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

HTML-конвейер

Spec: CSS Cascade 5, §6.1Spec: CSS Display 3, §2

NextPDF отрисовывает HTML и CSS в PDF прямо внутри вашего PHP-процесса: без браузера и без подпроцесса по умолчанию. На этой странице описаны многоуровневые этапы преобразования, реальный охват CSS-движка и случаи, когда делегирование настоящему рендереру браузера — честный выбор.

“HTML в PDF” звучит как одна операция. На самом деле это каскад, блочная модель, проход макета и проход отрисовки. Каждый из них — чётко определённая задача со своими режимами отказа. Движок, который сливает их в одну процедуру, хрупок: изменение в разборе цвета может сместить блок, и единственный способ это заметить — отрисовать и посмотреть.

У модели “внутри процесса” есть реальное преимущество: не нужно устанавливать браузер, поддерживать песочницу и передавать данные через границу процессов. Но это окупается только тогда, когда преобразование разложено достаточно чисто, чтобы каждую задачу можно было тестировать по отдельности. Именно архитектура делает “отрисовку HTML на PHP” не просто возможной, а заслуживающей доверия.

  • Преобразование HTML/CSS выполняется внутри процесса через writeHtml(). Результат — нативное содержимое PDF, а не изображение страницы.
  • Оно работает в один проход и потоково. Токенизатор формирует список токенов. Парсер обрабатывает его слева направо, и полное дерево DOM не сохраняется (ADR-001). Жёсткие пределы ограничивают количество элементов и глубину вложенности.
  • Движок построен как явные слои: разбор CSS и применители, состояние стилей, макет и форматирование, отрисовка и постраничные носители — со строгими правилами о том, что может делать каждый слой (ADR-010).
  • CSS-движок охватывает каскад, блочную модель и распространённые виды макета (блочный, строчный, таблицы, плавающие элементы и другие) — это значительный, но строго определённый поднабор того, что реализует современный браузер.
  • Когда требуется точное браузерное соответствие для произвольного современного CSS, NextPDF может делегировать рендереру headless-браузера через дополнительное расширение — это продуманный, изолированный от сети шов, а не путь по умолчанию.

Преобразование — это последовательность этапов, каждый из которых принимает типизированный вывод предыдущего этапа.

  1. TokenizeHTML becomes an ordered token list — no retained DOM tree.
  2. Resolve CSSParse styles; the cascade and applicators compute typed values.
  3. Style stateA push/pop style stack carries computed values per nesting level.
  4. LayoutBlock, inline, table, and float geometry computed; no paint here.
  5. PaintBorders, backgrounds, text, and decorations emit PDF operators.
  6. Paged mediaPage-break and @page rules applied as the cursor crosses page bounds.
Внутрипроцессный конвейер HTML: один проход слева направо по потоку токенов, где разрешение CSS, состояние стилей, макет и отрисовка — отдельные слои, а разрывы постраничных носителей применяются по мере продвижения курсора.

Два архитектурных правила превращают этот поток в устойчивую архитектуру.

У слоёв есть контракты. Текст CSS читается только внутри классов-применителей. Код макета вычисляет геометрию, но не порождает операторов отрисовки. Код отрисовки читает неизменяемый снимок вычисленных стилей, но никогда — изменяемое состояние отслеживания макета. Код постраничных носителей инициирует разрывы, но передаёт оформление страницы слою отрисовки. Эти границы соблюдаются принудительно (ADR-010). Поэтому новое свойство CSS — это новый применитель, а не изменение, которое сразу расползается по парсеру, диспетчеру макета и средству отрисовки.

DOM отсутствует. По решению (ADR-001) конвейер работает в один проход и потоково: не более одного состояния стиля на уровень вложенности плюс активный курсор, а не один объект на элемент. Некоторым операциям действительно нужно заглядывание вперёд — определение ширины столбцов таблицы, :has(), :last-child. Это обрабатывается ограниченными индексными структурами предварительного сканирования по плоскому списку токенов, а не сохранением дерева. Количество элементов и глубина вложенности жёстко ограничены, поэтому патологический ввод быстро завершается ошибкой, а не исчерпывает память.

CSS-движок разрешает настоящую семантику CSS, а не её подобие. Конкурирующие объявления сводятся к одному значению на свойство по источнику, важности, слою, специфичности и порядку — это и есть настоящий каскад. Макет следует блочной модели. Тип блока и контекст форматирования, который он создаёт, определяют, как размещаются он сам и его соседние элементы в потоке. Исходный код движка организован именно вокруг этих задач (каскад, box/display, flex, float, таблицы, фрагментация). Поэтому вы можете рассуждать о его поведении на основе спецификаций, а не выяснять его опытным путём.

Отрисовка внутри процесса — это один вызов. Результат — выделяемый текст PDF, а не растрированная страница:

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$html = <<<'HTML'
<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML and CSS</strong> directly into PDF pages,
<em>in-process</em>.</p>
<ul>
<li>Headings, paragraphs, bold and italic</li>
<li>Lists, tables, inline styles</li>
</ul>
HTML;
$doc->writeHtml($html);
$doc->save(__DIR__ . '/html-basic.pdf');

Если бы тому же документу потребовался произвольный современный CSS с точным браузерным соответствием, вместо этого был бы вызван writeHtmlChrome($html) — тот же документ, другой путь отрисовки и сознательная зависимость от дополнительного рендерера браузера.

Распространённое заблуждение — будто движок HTML в PDF “по сути своей браузер”. Это не так, и он на это не претендует. Браузер — это обширная, непрерывно обновляемая реализация всей веб-платформы. Внутрипроцессный движок NextPDF — это согласованный со спецификациями поднабор, сосредоточенный на макете документа. Честная мысленная модель — “грамотный CSS-движок для печатных документов”, а не “Chrome на PHP”. Когда вам действительно нужна вся платформа, для этого и предназначен writeHtmlChrome(). Это отдельный, подключаемый по выбору путь со своим эксплуатационным следом, а не тихий запасной вариант.

Второе заблуждение: считать, что путь через браузер — это просто “отрисовка страницы по сети”. По устройству это ровно наоборот. Шов делегирования всегда выполняет отрисовку с заблокированным сетевым доступом к подресурсам — без удалённых изображений, шрифтов, таблиц стилей и фреймов, — поэтому он не может стать вектором исходящих запросов. Попиксельная точность — да; открытый сетевой выход — нет.

На этой странице описаны устройство конвейера и выбор между работой внутри процесса и браузером. Это не матрица поддержки CSS. Какие именно свойства, модули и селекторы охватывает внутрипроцессный движок, определяется кодом и его тестами на соответствие, а не этим обзором. Этот охват развивается. Путь делегирования браузеру требует дополнительного расширения и двоичного файла Chrome/Chromium. Его настройка, эксплуатационные характеристики и внутренняя структура расширения выходят за рамки данной страницы и описаны вместе с этим пакетом. “Внутри процесса” описывает путь writeHtml() по умолчанию. Это не утверждение о том, что любой путь отрисовки обходится без подпроцесса. Архитектурные утверждения верны на дату проверки этой страницы. Авторитетные источники — это src/Html/, ADR-001 и ADR-010 в основном репозитории.

Внутрипроцессный движок CSS — это возможность редакции Core. Шов делегирования браузеру — это дополнительное расширение, представленное здесь только на уровне возможностей:

HTML rendering paths (Пути отрисовки HTML) — edition availability
EditionAvailability
CoreCore предоставляет внутрипроцессный движок HTML/CSS (writeHtml).
ProПуть делегирования браузеру — это дополнительное подключаемое расширение, не зависящее от уровня редакции.
EnterpriseПуть делегирования браузеру — это дополнительное подключаемое расширение, не зависящее от уровня редакции.
  • Отрисовка внутри процесса — преобразование HTML/CSS в PDF внутри процесса PHP, без браузера и подпроцесса по умолчанию (writeHtml()).
  • Однопроходная / потоковая обработка — обработка потока токенов слева направо без сохранения полного дерева DOM (ADR-001).
  • Каскад — процесс CSS, который разрешает конкурирующие объявления в одно значение на свойство по источнику, важности, слою, специфичности и порядку.
  • Контекст форматирования — среда макета, которую создаёт блок и которая определяет, как размещается его содержимое в потоке.
  • Контракт слоёв движка — принудительно соблюдаемый набор правил (ADR-010), определяющий, что может делать каждый из слоёв разбора, стилей, макета, отрисовки и постраничных носителей.
  • Шов делегирования браузеру — дополнительный путь writeHtmlChrome(), который выполняет отрисовку через headless-браузер с заблокированным сетевым доступом к подресурсам.