Accesibilidad: primitivas de etiquetado y modelo de estructura de PDF/UA-2
De un vistazo
Sección titulada «De un vistazo»NextPDF Core proporciona primitivas que respaldan la creación accesible: un árbol de estructura lógico, asignación de roles estándar, etiquetado de contenido marcado y atributos de idioma BCP-47 coherentes con el modelo de árbol de estructura definido en ISO 14289-2 (PDF/UA-2) e ISO 32000-2 §14.7. La conformidad del archivo generado es una propiedad del documento final, de las decisiones de contenido del autor y de un comprobador externo. No es una garantía que la biblioteca afirme en nombre de quien crea el documento.
Instalación
Sección titulada «Instalación»composer require nextpdf/coreResumen conceptual
Sección titulada «Resumen conceptual»Un PDF etiquetado contiene un árbol de estructura lógico cuya raíz incluye un único elemento de estructura Document. La tecnología de asistencia lee ese árbol para derivar un orden de lectura significativo, independiente de la disposición visual (ISO 32000-2 §14.7.2; ISO 14289-2 §8.2.5.2). NextPDF modela este comportamiento con tres tipos que colaboran entre sí en el espacio de nombres NextPDF\Accessibility.
StructureTree es el propietario de la jerarquía. Asigna identificadores de contenido marcado por página, hace el seguimiento del anidamiento entre padres e hijos, y serializa la raíz del árbol de estructura, los elementos de estructura, el árbol de padres, el mapa de roles y el espacio de nombres de estructura estándar de PDF 2.0 según ISO 32000-2 §14.7. createRoot() inicializa el único elemento Document obligatorio con un atributo de idioma. addElement() adjunta hijos tipados. hasRoot() y rootHasChildren() indican si el árbol existe y si tiene descendientes.
StructureElement es el objeto de valor de un diccionario de elemento de estructura. Captura el tipo de estructura estándar (nombres de la tabla 368 como H1 a H6, P, L, LI, Table, Figure, Link), las entradas de identificador de contenido marcado y los atributos de accesibilidad opcionales de texto alternativo, texto de reemplazo, título e idioma. Un único elemento puede abarcar varias páginas y acumular una entrada de identificador por página, de modo que la matriz de elementos secundarios referencie el contenido marcado a través de los límites de página.
TaggedContentEmitter conecta la canalización de HTML con el árbol de estructura. Cuando Document::enableTaggedPdf() está activo, el motor de renderizado de HTML conecta el emisor para que los elementos de nivel de bloque produzcan operadores de contenido marcado emparejados con los nodos de elemento de estructura correspondientes. HtmlToStructureMap proporciona la asignación tabular de cada etiqueta HTML al tipo de estructura PDF (ISO 14289-2 §8). El emisor enruta el contenido decorativo recurrente, como las regiones de encabezado y pie de página del HTML, a un artefacto, lo que lo mantiene fuera del orden de lectura.
Bcp47Validator valida el etiquetado de idioma (RFC 5646). Ofrece una comprobación sintáctica de buena formación y una comprobación de validez respaldada por el registro. El modo estricto (ConformancePolicy::strictUa2()) rechaza las etiquetas mal formadas en el límite de la API en lugar de descartarlas de forma silenciosa durante la escritura. Esto coincide con el requisito de ISO 14289-2 §8.4.4 de que la entrada de idioma del catálogo se resuelva en un idioma específico.
Superficie de la API
Sección titulada «Superficie de la API»| Símbolo | Tipo | Resumen |
|---|---|---|
Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): static | método | Activa el árbol de estructura y el puente de HTML; establece las entradas de información de marcado y de idioma del catálogo. |
Document::setLanguage(string $lang): static | método | Establece el idioma natural a nivel de documento (BCP-47). |
Document::isTaggedPdfEnabled(): bool | método | Indica si el modo de conformidad activo exige etiquetado estructural. |
StructureTree::createRoot(string $lang = 'en'): int | método | Crea el único elemento raíz Document obligatorio. |
StructureTree::addElement(int $parentIndex, string $type, int $pageIndex, ...): int | método | Adjunta un elemento de estructura secundario tipado. |
StructureTree::hasRoot(): bool y rootHasChildren(): bool | método | Comprueba si el árbol existe y si tiene descendientes. |
StructureElement | clase final | Objeto de valor para un elemento de estructura (texto alternativo, texto de reemplazo, título, idioma, identificadores). |
RoleMap::standard(): array<string,string> | estático | Vocabulario de tipos de estructura estándar (ISO 32000-2 tabla 368 más los tipos de PDF 2.0). |
Bcp47Validator::isWellFormed/isValid/validate/normalise | método | Validación sintáctica de etiquetas de idioma y validación respaldada por el registro de RFC 5646. |
AccessibilityAutoFixerRegistry | clase final | Registro opcional al estilo PSR-11 para correctores heurísticos de estructura. |
Ejemplo de código — Inicio rápido
Sección titulada «Ejemplo de código — Inicio rápido»<?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');Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»<?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); }}Casos límite y trampas
Sección titulada «Casos límite y trampas»- Orden de las llamadas. Llamar a
enableTaggedPdf()antes dewriteHtml(). La canalización de HTML comprueba el modo de conformidad al construir el analizador y no conecta el emisor de forma retroactiva para el contenido ya renderizado. - Árbol de estructura vacío. Un documento con
enableTaggedPdf()pero sin descendientes de estructura adjuntos no anuncia PDF/UA-2 en sus metadatos. La compuerta de publicación esrootHasChildren(), nohasRoot(), porque los validadores rechazan un archivo que declara PDF/UA-2 con un árbol de estructura vacío (ISO 14289-2 §5; verificado porEmptyTaggedPdfDoesNotAdvertisePdfUa2Test). - Colapso del modo de conformidad. Llamar a
enablePdfA()yenableTaggedPdf()en el mismo documento colapsa el discriminador de conformidad de valor único a la modalidad en la que prevalece la última llamada. Los efectos secundarios (árbol de estructura, información de marcado) siguen siendo aditivos, y se emite una advertenciaCONFORMANCE_MODE_CLOBBEREDpara que el colapso sea observable. - Los correctores automáticos no son automáticos. Los correctores integrados (
EmptyTagStripper,LegacyLangNormaliser,RootLangFallback) se distribuyen bajoNextPDF\Accessibility\AutoFixer\*, pero nunca se registran de forma automática. El consumidor debe registrarlos explícitamente enAccessibilityAutoFixerRegistry.
Limitaciones conocidas
Sección titulada «Limitaciones conocidas»NextPDF emite una estructura coherente con el modelo de árbol de estructura de PDF/UA-2, pero no crea de forma automática semántica que no puede inferir. Lo siguiente requiere marcado o atributos proporcionados por el autor y no se genera automáticamente:
- texto alternativo para imágenes y otro contenido no textual;
- el ámbito de los encabezados de tabla y la asociación entre encabezado y celda más allá de lo que expresa el marcado HTML;
- texto del propósito del enlace cuando el texto visible del enlace no se describe por sí mismo;
- semántica de lista para el contenido dispuesto visualmente como una lista sin marcado de lista;
- un orden de lectura corregido cuando el orden de origen difiere del orden de lectura previsto;
- la clasificación de contenido ambiguo como decorativo o significativo.
La biblioteca no realiza ninguna verificación de extremo a extremo de PDF/UA-2. El propio entorno de ejecución emite un aviso de Degraded / ComplianceRisk (PDFUA2_FOUNDATIONAL) que indica a quien realiza la llamada que valide la salida con un comprobador externo antes de aprobarla para producción. Validar con un comprobador de PDF/UA (por ejemplo, veraPDF). NextPDF no afirma la conformidad en nombre de quien crea el documento. La conformidad del documento final depende de las decisiones de creación y de un validador, no de llamar a la API.
Rendimiento
Sección titulada «Rendimiento»La construcción del árbol de estructura es lineal en función del número de elementos de estructura. La asignación de identificadores tiene tiempo constante amortizado por cada secuencia de contenido marcado. La serialización es una única pasada lineal sobre el conjunto de elementos. El costo dominante del etiquetado impulsado por HTML es la propia canalización de HTML, no la emisión de etiquetas. El límite por recipe declarado en performance_budget (1500 ms de tiempo de reloj, 64 MB de pico) se aplica a un documento semántico típico de varias páginas. Los documentos grandes escalan de forma lineal con el número de elementos, no con el número de páginas.
Notas de seguridad
Sección titulada «Notas de seguridad»Las etiquetas de idioma y los atributos de accesibilidad fluyen hacia los objetos de nombre y de cadena de PDF. NextPDF los escapa mediante PdfStringEscaper, de modo que los valores de idioma, texto alternativo, texto de reemplazo y título mal formados u hostiles no puedan escapar de su contexto de objeto PDF. Además, el modo estricto rechaza las etiquetas BCP-47 no registradas en el límite de la API, lo que reduce la superficie de entrada antes de que llegue al escritor. Los atributos de accesibilidad pueden contener texto libre proporcionado por el autor. Tratarlos como salida no confiable y aplicarles la misma revisión que a otro contenido del documento. Consultar el módulo de Conformidad para conocer el comportamiento del comprobador de perfiles.
Conformidad
Sección titulada «Conformidad»Esta página asigna el comportamiento de la biblioteca a identificadores de cláusula. No afirma que la salida sea conforme. Las cláusulas citadas se parafrasean; nunca se citan textualmente. Consultar la asignación de la especificación PDF/UA-2 para ver la tabla a nivel de provisión y la no cobertura explícita. Los hashes de los fragmentos de cita se registran en docs/public/modules/core/_normative-evidence-a11y.md.