Доступность: примитивы тегирования и модель структуры PDF/UA-2
Краткий обзор
Заголовок раздела «Краткий обзор»NextPDF Core предоставляет примитивы для создания доступных документов: дерево логической структуры, стандартное сопоставление ролей, тегирование размеченного содержимого и языковые атрибуты Best Current Practice (BCP) 47, согласованные с моделью дерева структуры в ISO 14289-2 (PDF/UA-2) и ISO 32000-2 §14.7. Итоговый файл соответствует требованиям только тогда, когда это подтверждают сам документ, выбор содержимого автором и внешнее средство проверки. Библиотека не даёт такую гарантию за вас.
Установка
Заголовок раздела «Установка»composer require nextpdf/coreКонцептуальный обзор
Заголовок раздела «Концептуальный обзор»Тегированный файл Portable Document Format (PDF) содержит дерево логической структуры, в корне которого находится единственный структурный элемент Document. Вспомогательные технологии читают это дерево, чтобы определить осмысленный порядок чтения, не зависящий от визуального макета (ISO 32000-2 §14.7.2; ISO 14289-2 §8.2.5.2). NextPDF моделирует это с помощью трёх взаимосвязанных типов в пространстве имён NextPDF\Accessibility.
StructureTree управляет иерархией. Он выделяет идентификаторы размеченного содержимого для каждой страницы, отслеживает вложенность родителей и потомков и сериализует корень дерева структуры, структурные элементы, дерево родителей, карту ролей и стандартное пространство имён структуры PDF 2.0 согласно ISO 32000-2 §14.7. createRoot() создаёт обязательный единственный элемент Document с языковым атрибутом. addElement() присоединяет типизированных потомков. hasRoot() и rootHasChildren() сообщают, существует ли дерево и есть ли у него потомки.
StructureElement — объект-значение для одного словаря структурного элемента. Он хранит стандартный тип структуры (имена из таблицы 368, такие как H1 — H6, P, L, LI, Table, Figure, Link), записи идентификаторов размеченного содержимого, а также необязательные атрибуты доступности: альтернативный текст, замещающий текст, заголовок и язык. Один элемент может занимать несколько страниц. Он накапливает по одной записи идентификатора на страницу, поэтому массив kids ссылается на размеченное содержимое через границы страниц.
TaggedContentEmitter связывает конвейер Hypertext Markup Language (HTML) с деревом структуры. Когда Document::enableTaggedPdf() активен, средство отрисовки HTML подключает эмиттер так, что блочные элементы создают парные операторы размеченного содержимого и соответствующие узлы структурных элементов. HtmlToStructureMap задаёт табличное сопоставление тегов HTML с типами структуры PDF (ISO 14289-2 §8). Эмиттер направляет декоративное колонтитульное содержимое, например области верхнего и нижнего колонтитулов HTML, в артефакт и исключает его из порядка чтения.
Bcp47Validator проверяет языковое тегирование (Request for Comments (RFC) 5646). Он проверяет синтаксическую правильность и валидность по реестру. Строгий режим (ConformancePolicy::strictUa2()) отклоняет некорректные теги на границе application programming interface (API), а не отбрасывает их без уведомления во время записи. Это соответствует требованию ISO 14289-2 §8.4.4: запись языка в каталоге должна разрешаться в конкретный язык.
Состав API
Заголовок раздела «Состав API»| Символ | Вид | Описание |
|---|---|---|
Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): static | метод | Активирует дерево структуры и мост HTML; задаёт записи mark-info и язык каталога. |
Document::setLanguage(string $lang): static | метод | Задаёт естественный язык документа (BCP-47). |
Document::isTaggedPdfEnabled(): bool | метод | Сообщает, требует ли активный режим соответствия структурного тегирования. |
StructureTree::createRoot(string $lang = 'en'): int | метод | Создаёт обязательный единственный корневой элемент Document. |
StructureTree::addElement(int $parentIndex, string $type, int $pageIndex, ...): int | метод | Присоединяет типизированный дочерний структурный элемент. |
StructureTree::hasRoot(): bool и rootHasChildren(): bool | метод | Сообщает, существует ли дерево и есть ли у него потомки. |
StructureElement | final class | Объект-значение для одного структурного элемента (альтернативный текст, замещающий текст, заголовок, язык, идентификаторы). |
RoleMap::standard(): array<string,string> | static | Возвращает стандартный словарь типов структуры (ISO 32000-2 таблица 368 плюс типы PDF 2.0). |
Bcp47Validator::isWellFormed/isValid/validate/normalise | метод | Проверяет языковые теги RFC 5646 синтаксически и по реестру. |
AccessibilityAutoFixerRegistry | final class | Подключаемый реестр в стиле PHP Standards Recommendation (PSR)-11 для эвристических средств исправления структуры. |
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»<?php
declare(strict_types=1);
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// The BCP 47 tag drives the catalog language entry and the// structure-tree root language attribute.$doc->enableTaggedPdf(lang: 'en');$doc->setTitle('Tagged accessibility demo');$doc->addPage();
// Semantic HTML maps to structure elements: h1 to /H1, p to /P,// ul and li to /L plus /LI. Text runs are wrapped in// marked-content operators with stable identifiers.$doc->writeHtml('<h1>Document title</h1><p>Body paragraph.</p>');
$doc->save(__DIR__ . '/output/tagged.pdf');Пример кода — рабочая среда
Заголовок раздела «Пример кода — рабочая среда»<?php
declare(strict_types=1);
use NextPDF\Conformance\ConformancePolicy;use NextPDF\Core\Document;use NextPDF\Exception\InvalidConfigException;use Psr\Log\LoggerInterface;
final class AccessibleReportWriter{ public function __construct(private readonly LoggerInterface $logger) { }
public function render(string $html, string $bcp47Lang, string $outPath): void { $doc = Document::createStandalone();
try { // strictUa2() rejects malformed BCP 47 tags at the API // boundary (ISO 14289-2 §8.4.4) instead of dropping silently. $doc->enableTaggedPdf($bcp47Lang, ConformancePolicy::strictUa2()); } catch (InvalidConfigException $e) { $this->logger->error('Rejected language tag for tagged PDF', [ 'lang' => $bcp47Lang, 'reason' => $e->getMessage(), ]);
throw $e; }
$doc->setTitle('Quarterly accessibility report') ->setLanguage($bcp47Lang) ->addPage();
$doc->writeHtml($html);
// The engine emits a Degraded / ComplianceRisk advisory directing // the caller to validate externally; surface it to operators // rather than treating tagged output as certified. foreach ($doc->getWarnings() as $warning) { $this->logger->warning('Tagged-PDF advisory', [ 'code' => $warning->code->value, 'message' => $warning->message, ]); }
$doc->save($outPath); }}Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Порядок вызовов. Вызывайте
enableTaggedPdf()передwriteHtml(). Конвейер HTML проверяет режим соответствия при создании парсера и не подключает эмиттер задним числом для содержимого, которое уже отрисовано. - Пустое дерево структуры. Документ с
enableTaggedPdf(), но без присоединённых потомков структуры не объявляет PDF/UA-2 в метаданных. Условием публикации служитrootHasChildren(), а неhasRoot(), потому что средства проверки отклоняют файл, который заявляет PDF/UA-2 с пустым деревом структуры (ISO 14289-2 §5; проверено вEmptyTaggedPdfDoesNotAdvertisePdfUa2Test). - Схлопывание режима соответствия. Когда вы вызываете
enablePdfA()иenableTaggedPdf()для одного и того же документа, одиночный дискриминатор соответствия схлопывается по принципу “выигрывает последний”. Побочные эффекты (дерево структуры, mark-info) остаются аддитивными, и NextPDF выдаёт предупреждениеCONFORMANCE_MODE_CLOBBERED, так что схлопывание остаётся наблюдаемым. - Средства автоисправления не работают автоматически. Встроенные средства исправления (
EmptyTagStripper,LegacyLangNormaliser,RootLangFallback) поставляются вNextPDF\Accessibility\AutoFixer\*, но никогда не регистрируются автоматически. Их нужно явно зарегистрировать вAccessibilityAutoFixerRegistry.
Известные ограничения
Заголовок раздела «Известные ограничения»NextPDF выдаёт структуру, согласованную с моделью дерева структуры PDF/UA-2, но не создаёт семантику, которую не может вывести. Вы должны предоставить разметку или атрибуты для перечисленного ниже; NextPDF не генерирует их за вас:
- альтернативный текст для изображений и другого нетекстового содержимого;
- область действия заголовков таблицы и связи заголовков с ячейками сверх того, что выражает разметка HTML;
- текст назначения ссылки, когда видимый текст ссылки не является самоописательным;
- семантику списка для содержимого, которое визуально оформлено как список, но не имеет разметки списка;
- исправленный порядок чтения, если исходный порядок отличается от предполагаемого;
- классификацию «декоративное против значимого» для неоднозначного содержимого.
NextPDF не выполняет сквозную проверку PDF/UA-2. Во время выполнения он выдаёт рекомендацию Degraded / ComplianceRisk (PDFUA2_FOUNDATIONAL), которая предписывает вызывающей стороне проверить вывод внешним средством проверки до утверждения для рабочей среды. Проверьте документ средством проверки PDF/UA (например, veraPDF). NextPDF не подтверждает соответствие от вашего имени. Соответствие итогового документа зависит от авторских решений и средства проверки, а не от вызова API.
Производительность
Заголовок раздела «Производительность»Построение дерева структуры линейно по числу структурных элементов. Выделение идентификаторов выполняется за амортизированное постоянное время на каждую последовательность размеченного содержимого. Сериализация — один линейный проход по набору элементов. Для тегирования на основе HTML основные затраты приходятся на сам конвейер HTML, а не на выдачу тегов. Предельное значение на рецепт, объявленное в performance_budget (1500 мс по времени и 64 МБ пика), относится к типичному многостраничному семантическому документу. Большие документы масштабируются линейно по числу элементов, а не по числу страниц.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»Языковые теги и атрибуты доступности попадают в объекты-имена и строковые объекты PDF. NextPDF экранирует их через PdfStringEscaper, поэтому некорректные или вредоносные значения языка, альтернативного текста, замещающего текста и заголовка не могут выйти из контекста своего объекта PDF. Строгий режим также отклоняет незарегистрированные теги BCP-47 на границе API, сужая поверхность ввода до того, как она достигнет средства записи. Атрибуты доступности могут содержать свободный текст, предоставленный автором. Относитесь к ним как к недоверенному выводу и проверяйте их так же, как проверяете остальное содержимое документа. См. модуль Conformance, чтобы узнать о поведении средства проверки профилей.
Соответствие
Заголовок раздела «Соответствие»На этой странице поведение библиотеки сопоставлено с идентификаторами пунктов. Она не утверждает, что ваш вывод соответствует требованиям. Цитируемые пункты пересказаны, а не приведены дословно. См. сопоставление со спецификацией PDF/UA-2, чтобы ознакомиться с таблицей на уровне положений и явным перечнем непокрытого. Хеши фрагментов цитат записаны в docs/public/modules/core/_normative-evidence-a11y.md.