Salta ai contenuti

Accessibilità: primitive di tagging e modello della struttura PDF/UA-2

NextPDF Core fornisce primitive per la creazione accessibile: un albero strutturale logico, la mappatura standard dei ruoli, il tagging del contenuto marcato e attributi di lingua BCP-47 coerenti con il modello di albero strutturale definito in ISO 14289-2 (PDF/UA-2) e ISO 32000-2 §14.7. La conformità del file prodotto è una proprietà del documento finale, delle scelte di contenuto dell’autore e di un validatore esterno. Non è una garanzia attestata dalla libreria per conto dell’utente.

Terminal window
composer require nextpdf/core

Un PDF taggato contiene un albero strutturale logico la cui radice contiene un singolo elemento di struttura Document. Le tecnologie assistive leggono questo albero per ricavare un ordine di lettura significativo, indipendente dal layout visivo (ISO 32000-2 §14.7.2; ISO 14289-2 §8.2.5.2). NextPDF modella tutto questo con tre tipi che collaborano nel namespace NextPDF\Accessibility.

StructureTree gestisce la gerarchia. Alloca gli identificatori del contenuto marcato per pagina. Tiene traccia dell’annidamento padre-figlio. Serializza la radice dell’albero strutturale, gli elementi di struttura, l’albero dei padri, la mappa dei ruoli e il namespace della struttura standard PDF 2.0 secondo ISO 32000-2 §14.7. createRoot() inizializza l’unico elemento Document obbligatorio con un attributo di lingua. addElement() collega elementi figli tipizzati. hasRoot() e rootHasChildren() indicano se l’albero esiste e se ha discendenti.

StructureElement è il value object per un singolo dizionario di elemento di struttura. Raccoglie il tipo di struttura standard (nomi della Tabella 368 come H1 fino a H6, P, L, LI, Table, Figure, Link), le voci degli identificatori del contenuto marcato e gli attributi di accessibilità opzionali per testo alternativo, testo sostitutivo, titolo e lingua. Un singolo elemento può estendersi su più pagine, accumulando una voce di identificatore per pagina, in modo che l’array dei figli faccia riferimento al contenuto marcato anche oltre i confini di pagina.

TaggedContentEmitter collega la pipeline HTML all’albero strutturale. Quando Document::enableTaggedPdf() è attivo, il renderer HTML collega l’emitter affinché gli elementi a livello di blocco producano operatori di contenuto marcato associati ai corrispondenti nodi di elemento di struttura. HtmlToStructureMap fornisce la mappatura table-driven dai tag HTML ai tipi di struttura PDF (ISO 14289-2 §8). L’emitter instrada il contenuto decorativo ricorrente, come le regioni di intestazione e piè di pagina HTML, verso un artefatto, così da mantenerlo fuori dall’ordine di lettura.

Il tagging linguistico è convalidato da Bcp47Validator (RFC 5646). Offre un controllo sintattico di corretta formazione e un controllo di validità basato sul registro. La modalità strict (ConformancePolicy::strictUa2()) rifiuta i tag malformati al confine dell’API, anziché scartarli silenziosamente in fase di scrittura. Ciò soddisfa il requisito di ISO 14289-2 §8.4.4 secondo cui la voce di lingua del catalogo deve risolversi in una lingua specifica.

SimboloTipoRiepilogo
Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): staticmetodoAttiva l’albero strutturale e il bridge HTML; imposta le voci mark-info e di lingua del catalogo.
Document::setLanguage(string $lang): staticmetodoImposta la lingua naturale a livello di documento (BCP-47).
Document::isTaggedPdfEnabled(): boolmetodoIndica se la modalità di conformità attiva impone il tagging strutturale.
StructureTree::createRoot(string $lang = 'en'): intmetodoCrea l’unico elemento radice Document obbligatorio.
StructureTree::addElement(int $parentIndex, string $type, int $pageIndex, ...): intmetodoCollega un elemento di struttura figlio tipizzato.
StructureTree::hasRoot(): bool e rootHasChildren(): boolmetodoVerifica se l’albero esiste e se ha discendenti.
StructureElementclasse finalValue object per un singolo elemento di struttura (testo alternativo, testo sostitutivo, titolo, lingua, identificatori).
RoleMap::standard(): array<string,string>staticVocabolario standard dei tipi di struttura (ISO 32000-2 Tabella 368 più i tipi PDF 2.0).
Bcp47Validator::isWellFormed/isValid/validate/normalisemetodoConvalida i tag di lingua RFC 5646 a livello sintattico e rispetto al registro.
AccessibilityAutoFixerRegistryclasse finalRegistro opt-in in stile PSR-11 per i correttori euristici della struttura.
<?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);
}
}
  • Ordine delle chiamate. Chiamare enableTaggedPdf() prima di writeHtml(). La pipeline HTML verifica la modalità di conformità al momento della costruzione del parser e non collega retroattivamente l’emitter al contenuto già renderizzato.
  • Albero strutturale vuoto. Un documento con enableTaggedPdf() ma senza discendenti di struttura collegati non dichiara PDF/UA-2 nei propri metadati. Il gate di pubblicazione è rootHasChildren(), non hasRoot(), perché un file che dichiara PDF/UA-2 con un albero strutturale vuoto viene rifiutato dai validatori (ISO 14289-2 §5; verificato da EmptyTaggedPdfDoesNotAdvertisePdfUa2Test).
  • Collasso della modalità di conformità. Chiamare enablePdfA() e enableTaggedPdf() sullo stesso documento riconduce il discriminatore di conformità a valore singolo all’approccio last-wins. Gli effetti collaterali (albero strutturale, mark-info) restano additivi e viene emesso un avviso CONFORMANCE_MODE_CLOBBERED, così che il collasso sia osservabile.
  • Gli auto-fixer non sono automatici. I fixer predefiniti (EmptyTagStripper, LegacyLangNormaliser, RootLangFallback) sono forniti in NextPDF\Accessibility\AutoFixer\* ma non vengono mai registrati automaticamente. Il codice chiamante deve registrarli esplicitamente su AccessibilityAutoFixerRegistry.

NextPDF emette una struttura coerente con il modello di albero strutturale PDF/UA-2, ma non genera automaticamente la semantica che non può inferire. I seguenti elementi richiedono markup o attributi forniti dall’autore e non vengono generati automaticamente:

  • testo alternativo per le immagini e per altri contenuti non testuali;
  • l’ambito delle intestazioni di tabella e l’associazione intestazione-cella oltre a quanto espresso dal markup HTML;
  • il testo che descrive lo scopo del collegamento quando il testo visibile del collegamento non è autoesplicativo;
  • la semantica di elenco per i contenuti disposti visivamente come elenco ma privi di markup di elenco;
  • un ordine di lettura corretto quando l’ordine di origine differisce dall’ordine di lettura previsto;
  • la classificazione tra contenuto decorativo e significativo per i contenuti ambigui.

La libreria non esegue alcuna verifica PDF/UA-2 end-to-end. Il runtime stesso emette un avviso Degraded / ComplianceRisk (PDFUA2_FOUNDATIONAL) che indica al chiamante di convalidare l’output con un validatore esterno per l’approvazione in produzione. Convalidare con un validatore PDF/UA (ad esempio, veraPDF). NextPDF non attesta la conformità per conto dell’utente. La conformità del documento finale dipende dalle scelte di creazione e da un validatore, non dalla chiamata all’API.

La costruzione dell’albero strutturale è lineare rispetto al numero di elementi di struttura. L’allocazione degli identificatori avviene in tempo costante ammortizzato per ogni sequenza di contenuto marcato. La serializzazione è una singola passata lineare sull’insieme degli elementi. Il costo prevalente del tagging guidato da HTML è la pipeline HTML stessa, non l’emissione dei tag. Il limite dichiarato per ricetta in performance_budget (1500 ms di tempo reale, 64 MB di picco) si applica a un tipico documento semantico multipagina. I documenti di grandi dimensioni scalano linearmente con il numero di elementi anziché con il numero di pagine.

I tag di lingua e gli attributi di accessibilità confluiscono negli oggetti PDF di tipo name e string. NextPDF esegue l’escape di tali valori tramite PdfStringEscaper in modo che valori di lingua, testo alternativo, testo sostitutivo e titolo malformati o ostili non possano uscire dal contesto del proprio oggetto PDF. La modalità strict rifiuta inoltre i tag BCP-47 non registrati al confine dell’API, restringendo la superficie di input prima che raggiunga il writer. Gli attributi di accessibilità possono contenere testo libero fornito dall’autore. Trattarli come output non attendibile e applicare lo stesso controllo previsto per gli altri contenuti del documento. Per il comportamento del controllore dei profili, vedere il modulo Conformance.

Questa pagina mappa il comportamento della libreria agli identificatori di clausola. Non attesta che l’output dell’utente sia conforme. Le clausole citate sono parafrasate, mai citate testualmente. Per la tabella a livello di disposizione e la mancata copertura esplicita, vedere la mappatura della specifica PDF/UA-2. Gli hash dei chunk delle citazioni sono registrati in docs/public/modules/core/_normative-evidence-a11y.md.