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

HTML: подсистема отрисовки HTML+CSS в PDF

Подсистема HTML преобразует HyperText Markup Language (HTML) и Cascading Style Sheets (CSS) в потоки содержимого Portable Document Format (PDF) за один прямой проход. Это самая крупная подсистема движка с самым высоким уровнем риска: 324 файла в каталоге src/Html/.

Окно терминала
composer require nextpdf/core:^3

Подсистема HTML — это однопроходный потоковый отрисовщик HTML+CSS в PDF. Её публичная поверхность — один метод: Document::writeHtml(). Внутри HtmlParser разбивает входные данные на токены, разрешает стили, рассчитывает макет и выводит операторы PDF за один прямой проход, не сохраняя дерево документа.

Важно чётко понимать область применения. Эта подсистема не является отрисовщиком с удержанием документа. Она не хранит граф элементов, не выполняет повторную раскладку уже записанного содержимого и не позволяет входным данным изменяться после начала разбора. Она реализует тщательно отобранное подмножество CSS, привязанное к фиксированным версиям спецификаций. Её устройство задают два Architecture Decision Records (ADR). ADR-001 определяет однопроходную потоковую модель и её ограничения. ADR-010 определяет четырёхслойный контракт (разбор CSS, состояние стилей, макет, отрисовка), а также дополнения для постраничной вёрстки и измерений.

В манифесте модуля HtmlParser отнесён к критическому уровню риска. Для пяти файлов задокументированы пометки об опасных зонах: оркестратор HtmlParser (потоковый токенизатор, более 1000 строк кода (LOC)), HtmlStyleState (более 100 полей свойств CSS со стековой моделью наследования), HtmlBlockHandler (диспетчеризация блоков, связанная с состоянием стилей), FlexLayoutEngine (полный расчёт размеров и раскладка flex) и TableParser (постраничное разбиение colspan/rowspan через разрывы страниц). Любые изменения здесь рассматривайте как работу, требующую режима планирования.

Используйте эту страницу как точку входа. См. pipeline для последовательности стадий, css-resolver для каскада и специфичности, layer-contracts-adr010 для границ слоёв и streaming-constraints-adr001 для модели без удержания дерева и её ограничений.

Текст справа налево и двунаправленный текст

Заголовок раздела «Текст справа налево и двунаправленный текст»

writeHtml() отрисовывает содержимое справа налево (RTL). Задайте CSS-свойство direction: rtl для тела документа, таблицы или любого элемента. Движок разрешает визуальный порядок с помощью алгоритма двунаправленного текста Unicode (UAX #9) через двунаправленный движок слоя типографики — см. Типографику для подробностей о BidiEngine. Смешанное содержимое из латиницы, арабского письма и чисел упорядочивается корректно, а число после арабского письма сохраняет свои цифры слева направо.

Арабское письмо также проходит контекстное формирование: движок выбирает начальную, срединную, конечную или изолированную форму каждой буквы и применяет лигатуру Lam-Alef. Для формирования нужен зарегистрированный шрифт, чья карта символов покрывает блок Arabic Presentation Forms-B; начертание только с латиницей, включая standard-14 шрифты, не может отрисовать арабское письмо. В таблицах каждая ячейка переупорядочивается и формируется независимо и выравнивается по начальному (правому) краю при direction: rtl. RTL применяется к арабскому, ивриту, персидскому и урду; иврит переупорядочивается, но не формируется.

Задавайте направление CSS-свойством direction — HTML-атрибут dir ему не сопоставляется. Горизонтальное выравнивание блочного и строчного текста вне таблиц, а также text-align: justify пока не применяются. Запускаемый пример арабского счёта и полный список текущих ограничений см. в Отрисовка справа налево арабского HTML.

СимволРасположениеНазначение
Document::writeHtml(string $html): staticsrc/Core/Concerns/HasTextOutput.phpПубличная точка входа. Отрисовывает HTML в текущей позиции курсора.
Document::createStandalone(): selfsrc/Core/Document.phpСоздаёт автономный документ.
HtmlParser::parse(string $html): HtmlRenderResultsrc/Html/HtmlParser.phpВнутренний оркестратор.
HtmlRenderResultsrc/Html/HtmlRenderResult.phpНеизменяемый результат: поток, конечный курсор и использованные шрифты.
DefaultHtmlSecurityPolicysrc/Html/DefaultHtmlSecurityPolicy.phpПолитика по умолчанию для тегов, атрибутов, CSS и Uniform Resource Locator (URL).
HtmlSecurityPolicyInterfacesrc/Contracts/HtmlSecurityPolicyInterface.phpКонтракт для пользовательских политик.

Источник: examples/08-html-basic.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$doc->writeHtml('<h1 style="color:#1E3A8A;">HTML Rendering</h1><p>Direct to PDF.</p>');
$doc->save(__DIR__ . '/output/08-html-basic.pdf');

Этот пример показывает табличный отчёт со встроенным блоком стилей на основе examples/09-html-table.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Exception\HtmlParsingException;
function renderInventory(string $rowsHtml, string $out): void
{
$doc = Document::createStandalone();
$doc->setTitle('Inventory');
$doc->addPage();
$html = '<style>table { width: 100%; } '
. 'th { background-color: #1E3A8A; color: #FFFFFF; }</style>'
. '<table border="1" cellpadding="5">' . $rowsHtml . '</table>';
try {
$doc->writeHtml($html);
} catch (HtmlParsingException $e) {
// Input cap, element cap (50,000), or nesting cap (100). Do not retry.
throw $e;
}
$doc->save($out);
}
  • Тщательно отобранное подмножество CSS. Поддержка зафиксирована для каждого модуля. Сверьтесь с матрицей поддержки CSS, прежде чем полагаться на то или иное свойство.
  • Жёсткие ограничения вызывают исключения. Превышение любого из ограничений — 10 MB на входные данные, 50,000 элементов или 100 уровней вложенности — вызывает HtmlParsingException. См. ограничения потоковой обработки.
  • Без повторной раскладки. Отрисовщик записывает вывод один раз в порядке документа; стили, объявленные позже, не могут изменить ранее записанный вывод.
  • :has() доступен только при включённой экспериментальной функции css.has.
  • Подсистема с критическим уровнем риска. Пять файлов помечены как опасные зоны. Используйте режим планирования для изменений в каталоге src/Html/.

Ограничения однопроходной потоковой обработки (ADR-001)

Заголовок раздела «Ограничения однопроходной потоковой обработки (ADR-001)»

Отрисовщик не хранит дерево документа и выполняет один прямой проход. Ограничения на элементы, вложенность и входные данные являются жёсткими пределами. Полное описание и контракт безопасности для воркеров см. в ограничениях потоковой обработки (ADR-001).

Разбор CSS, состояние стилей, макет и отрисовка разделены на четыре слоя с однонаправленными контрактами и дополнениями для постраничной вёрстки и измерений. Полное описание см. в контрактах слоёв (ADR-010).

Память для состояния стилей и курсора имеет порядок O(глубина вложенности), а не O(количество элементов). Значение performance_budget на страницу равно peak_mb: 64. Ограничение в 50,000 элементов — это жёсткий потолок; разбивайте более крупные входные данные на несколько вызовов writeHtml(). Подробности см. в ограничениях потоковой обработки.

Обход имеет сложность O(количество токенов). Расчёт ширины столбцов таблицы добавляет ограниченное сканирование строк для каждой таблицы. Необязательное предварительное сканирование :has() добавляет один ограниченный проход по списку токенов. Бенчмарк производительности конвейера отрисовки HTML применяет порог регрессии в 5% (изменения объединены в pull request (PR) #564). Значение performance_budget на страницу (wall_ms: 1500, peak_mb: 64) является эксплуатационным потолком.

DefaultHtmlSecurityPolicy применяет список разрешённых тегов, атрибутов, свойств CSS и схем URL, а также потолок входных данных в 10 MB и потолок вложенности в 100 уровней, независимо от парсера. Список разрешённых свойств CSS задаёт потолок безопасности. Таблица поддержки во время выполнения задаёт отдельный потолок возможностей. Реализуйте HtmlSecurityPolicyInterface, чтобы задать более строгую политику. DefaultExternalResourcePolicy отдельно управляет загрузкой внешних ресурсов.

В значениях href и src для изображений список разрешённых URL также отклоняет пути, начинающиеся с обратной косой черты (\…), и пути Universal Naming Convention (UNC) (\\host\share), наряду с уже существующим отклонением протокол-относительных путей (//) и списком, разрешающим только http(s) или относительные пути. Перед проверкой обратные косые черты нормализуются в прямые, поэтому включение локального файла по абсолютному пути Windows или загрузка с общего ресурса Server Message Block (SMB) не может пройти через ветвь “нет схемы, значит относительный путь”. Ни один из этих путей не содержит схему Uniform Resource Identifier (URI).

Фрагмент матрицы поддержки CSS (только проверенные строки)

Заголовок раздела «Фрагмент матрицы поддержки CSS (только проверенные строки)»

Эта страница не дублирует сведения о поддержке отдельных свойств. Матрица поддержки CSS — единственный источник истины о проверенном статусе по каждому модулю World Wide Web Consortium (W3C), в том числе о том, какие модули проверены (Verified), а какие заявлены (Claimed).

Подсистема реализует тщательно отобранное подмножество CSS, привязанное к фиксированным версиям спецификаций. Сопоставления поведения каскада со спецификацией задокументированы с идентификаторами пунктов и фрагментов на странице css-resolver. Статус соответствия по каждому модулю приведён в матрице поддержки CSS.

Возможность корпоративного уровня. Premium расширяет охват CSS (расширенная печать и дополнительные модули) в рамках того же однопроходного конвейера. Архитектура, ограничения и контракты слоёв остаются одинаковыми во всех редакциях. См. матрицу поддержки CSS.