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.
Поверхность API
Заголовок раздела «Поверхность API»| Символ | Расположение | Назначение |
|---|---|---|
Document::writeHtml(string $html): static | src/Core/Concerns/HasTextOutput.php | Публичная точка входа. Отрисовывает HTML в текущей позиции курсора. |
Document::createStandalone(): self | src/Core/Document.php | Создаёт автономный документ. |
HtmlParser::parse(string $html): HtmlRenderResult | src/Html/HtmlParser.php | Внутренний оркестратор. |
HtmlRenderResult | src/Html/HtmlRenderResult.php | Неизменяемый результат: поток, конечный курсор и использованные шрифты. |
DefaultHtmlSecurityPolicy | src/Html/DefaultHtmlSecurityPolicy.php | Политика по умолчанию для тегов, атрибутов, CSS и Uniform Resource Locator (URL). |
HtmlSecurityPolicyInterface | src/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).
Контракты слоёв (ADR-010)
Заголовок раздела «Контракты слоёв (ADR-010)»Разбор 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.