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

HTML-конвейер

Spec: CSS Cascade 5, §6.1 Spec: CSS Display 3, §2 Evidence: Code-backed

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. Tokenize HTML becomes an ordered token list — no retained DOM tree.
  2. Resolve CSS Parse styles; the cascade and applicators compute typed values.
  3. Style state A push/pop style stack carries computed values per nesting level.
  4. Layout Block, inline, table, and float geometry computed; no paint here.
  5. Paint Borders, backgrounds, text, and decorations emit PDF operators.
  6. Paged media Page-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, таблицы, фрагментация). Поэтому вы можете рассуждать о его поведении на основе спецификаций, а не выяснять его опытным путём.

Эта страница Evidence: Code-backed . Этапы и правила соответствуют основному репозиторию:

  • Точка входа для работы внутри процесса — writeHtml(string $html): static в src/Core/Concerns/HasTextOutput.php.
  • Однопроходная схема без сохранения DOM с пределами на элементы и вложенность — это ADR-001 и код tokenizer/parser/стека стилей в src/Html/.
  • Контракт слоистого движка — parsing/applicators CSS, состояние стилей, макет, отрисовка, постраничные носители — это ADR-010, отражённый в структуре src/Html/ (например, Cascade/, Css/, Flex/, Float/, Fragmentation/ и классы-применители).
  • Шов делегирования браузеру — это writeHtmlChrome() в том же файле; согласно документации, ему требуется дополнительное расширение рендерера и двоичный файл Chrome/Chromium.

Стандарты честно подкрепляют формулировку об охвате. Каскад сводит конкурирующие объявления к единственному значению на свойство — источник, важность, слой, специфичность, порядок — согласно Spec: CSS Cascade 5, §6.1 , а размещение в потоке следует правилам блока и контекста форматирования согласно Spec: CSS Display 3, §2 . Не менее важна и граница: запрос возможностей существует именно потому, что не каждый обработчик поддерживает каждую возможность согласно Spec: CSS Conditional 5, §2 . Движок CSS в NextPDF — это определённый, согласованный со спецификациями поднабор, и прямо говорить об этом — часть контракта.

Отрисовка внутри процесса — это один вызов. Результат — выделяемый текст 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
Edition Availability
Core Core предоставляет внутрипроцессный движок HTML/CSS (writeHtml).
Pro Путь делегирования браузеру — это дополнительное подключаемое расширение, не зависящее от уровня редакции.
Enterprise Путь делегирования браузеру — это дополнительное подключаемое расширение, не зависящее от уровня редакции.
  • Отрисовка внутри процесса — преобразование HTML/CSS в PDF внутри процесса PHP, без браузера и подпроцесса по умолчанию (writeHtml()).
  • Однопроходная / потоковая обработка — обработка потока токенов слева направо без сохранения полного дерева DOM (ADR-001).
  • Каскад — процесс CSS, который разрешает конкурирующие объявления в одно значение на свойство по источнику, важности, слою, специфичности и порядку.
  • Контекст форматирования — среда макета, которую создаёт блок и которая определяет, как размещается его содержимое в потоке.
  • Контракт слоёв движка — принудительно соблюдаемый набор правил (ADR-010), определяющий, что может делать каждый из слоёв разбора, стилей, макета, отрисовки и постраничных носителей.
  • Шов делегирования браузеру — дополнительный путь writeHtmlChrome(), который выполняет отрисовку через headless-браузер с заблокированным сетевым доступом к подресурсам.