Ga naar inhoud

Toegankelijkheid: taggingprimitieven en het PDF/UA-2-structuurmodel

NextPDF Core biedt primitieven voor toegankelijk auteurschap: een logische structuurboom, standaard role mapping, marked-content-tagging en taalattributen volgens Best Current Practice (BCP) 47 die aansluiten op het structuurboommodel in ISO 14289-2 (PDF/UA-2) en ISO 32000-2 §14.7. Het geproduceerde bestand is alleen conform wanneer het uiteindelijke document, de inhoudskeuzes van de auteur en een externe controleur dat resultaat ondersteunen. De bibliotheek garandeert dat niet namens u.

Terminal window
composer require nextpdf/core

Een getagd Portable Document Format (PDF)-bestand bevat een logische structuurboom waarvan de root precies één Document-structuurelement bevat. Hulptechnologie gebruikt die boom om een betekenisvolle leesvolgorde te bepalen die niet afhangt van de visuele lay-out (ISO 32000-2 §14.7.2; ISO 14289-2 §8.2.5.2). NextPDF modelleert dit met drie samenwerkende typen in de namespace NextPDF\Accessibility.

StructureTree beheert de hiërarchie. Het wijst per pagina marked-content-identifiers toe, houdt de nesting van ouders en kinderen bij en serialiseert de structuurboom-root, de structuurelementen, de parent tree, de role map en de PDF 2.0-namespace voor standaardstructuur volgens ISO 32000-2 §14.7. createRoot() initialiseert het verplichte enkele Document-element met een taalattribuut. addElement() koppelt getypeerde onderliggende elementen. hasRoot() en rootHasChildren() melden of de boom bestaat en of die afstammelingen heeft.

StructureElement is het value-object voor één structuurelement-dictionary. Het bewaart het standaardstructuurtype (Table 368-namen zoals H1 tot en met H6, P, L, LI, Table, Figure, Link), marked-content-identifier-vermeldingen en optionele toegankelijkheidsattributen voor alternatieve tekst, vervangende tekst, titel en taal. Eén element kan zich over meerdere pagina’s uitstrekken. Het verzamelt één identifier-vermelding per pagina, zodat de kids-array naar marked content over paginagrenzen heen verwijst.

TaggedContentEmitter verbindt de Hypertext Markup Language (HTML)-pijplijn met de structuurboom. Wanneer Document::enableTaggedPdf() actief is, koppelt de HTML-renderer de emitter zodat block-level-elementen gepaarde marked-content-operators en bijbehorende structuurelement-knooppunten aanmaken. HtmlToStructureMap levert de tabelgestuurde mapping van HTML-tags naar PDF-structuurtypen (ISO 14289-2 §8). De emitter routeert decoratieve doorlopende inhoud, zoals de HTML-koptekst- en voettekstgebieden, naar een artefact en houdt die buiten de leesvolgorde.

Bcp47Validator valideert taaltagging (Request for Comments (RFC) 5646). Het biedt een syntactische well-formed-controle en een geldigheidscontrole op basis van het register. In strikte modus (ConformancePolicy::strictUa2()) worden onjuist opgemaakte tags geweigerd bij de grens van de application programming interface (API), in plaats van ze tijdens het schrijven stilzwijgend te verwijderen. Dit sluit aan op de eis in ISO 14289-2 §8.4.4 dat de taalvermelding in de catalogus naar een specifieke taal verwijst.

SymboolSoortSamenvatting
Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): staticmethodeActiveer de structuurboom en de HTML-brug; stel de taalvermeldingen voor mark-info en catalogus in.
Document::setLanguage(string $lang): staticmethodeStel de natuurlijke taal op documentniveau in (BCP 47).
Document::isTaggedPdfEnabled(): boolmethodeMeld of de actieve conformiteitsmodus structurele tagging vereist.
StructureTree::createRoot(string $lang = 'en'): intmethodeMaak het verplichte enkele Document-rootelement aan.
StructureTree::addElement(int $parentIndex, string $type, int $pageIndex, ...): intmethodeKoppel een getypeerd onderliggend structuurelement.
StructureTree::hasRoot(): bool en rootHasChildren(): boolmethodeMeld of de boom bestaat en of die afstammelingen heeft.
StructureElementfinal classValue-object voor één structuurelement (alternatieve tekst, vervangende tekst, titel, taal, identifiers).
RoleMap::standard(): array<string,string>staticRetourneer de woordenlijst met standaardstructuurtypen (ISO 32000-2 Table 368 plus PDF 2.0-typen).
Bcp47Validator::isWellFormed/isValid/validate/normalisemethodeValideer RFC 5646-taaltags met syntactische controles en controles op basis van het register.
AccessibilityAutoFixerRegistryfinal classOpt-in PHP Standards Recommendation (PSR)-11-achtig register voor heuristische structuurfixers.
<?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);
}
}
  • Volgorde van aanroepen. Roep enableTaggedPdf() aan vóór writeHtml(). De HTML-pijplijn controleert de conformiteitsmodus wanneer de parser wordt geconstrueerd en koppelt de emitter niet achteraf voor inhoud die al is gerenderd.
  • Lege structuurboom. Een document met enableTaggedPdf() maar zonder gekoppelde structuurafstammelingen vermeldt PDF/UA-2 niet in zijn metadata. De publicatiepoort is rootHasChildren(), niet hasRoot(), omdat validators een bestand weigeren dat PDF/UA-2 claimt terwijl de structuurboom leeg is (ISO 14289-2 §5; geverifieerd door EmptyTaggedPdfDoesNotAdvertisePdfUa2Test).
  • Instorting van de conformiteitsmodus. Wanneer je enablePdfA() en enableTaggedPdf() op hetzelfde document aanroept, valt de single-valued conformiteitsdiscriminator terug op last-wins. Neveneffecten (structuurboom, mark-info) blijven additief, en NextPDF genereert een CONFORMANCE_MODE_CLOBBERED-waarschuwing zodat de instorting zichtbaar is.
  • Auto-fixers zijn niet automatisch. Ingebouwde fixers (EmptyTagStripper, LegacyLangNormaliser, RootLangFallback) worden geleverd onder NextPDF\Accessibility\AutoFixer\*, maar worden nooit automatisch geregistreerd. Je moet ze expliciet registreren op AccessibilityAutoFixerRegistry.

NextPDF genereert structuur die consistent is met het PDF/UA-2-structuurboommodel, maar creëert geen semantiek die het niet kan afleiden. Voor het volgende moet je markup of attributen aanleveren; NextPDF genereert die niet voor je:

  • alternatieve tekst voor afbeeldingen en andere niet-tekstuele inhoud;
  • tabelheader-scope en koppelingen van header naar cel die verder gaan dan wat de HTML-markup uitdrukt;
  • tekst voor het doel van een link wanneer de zichtbare linktekst niet zelfbeschrijvend is;
  • lijstsemantiek voor inhoud die visueel als een lijst is opgemaakt maar geen lijst-markup heeft;
  • gecorrigeerde leesvolgorde wanneer de bronvolgorde afwijkt van de beoogde leesvolgorde;
  • classificatie van decoratief versus betekenisvol voor dubbelzinnige inhoud.

NextPDF voert geen end-to-end-PDF/UA-2-verificatie uit. Tijdens runtime genereert het een Degraded / ComplianceRisk-melding (PDFUA2_FOUNDATIONAL) die de aanroeper opdraagt de uitvoer met een externe controleur te valideren vóór goedkeuring voor productie. Valideer met een PDF/UA-controleur (bijvoorbeeld veraPDF). NextPDF garandeert geen conformiteit namens je. De conformiteit van het uiteindelijke document hangt af van auteurschapskeuzes en een validator, niet van het aanroepen van de API.

De constructie van de structuurboom is lineair in het aantal structuurelementen. De toewijzing van identifiers verloopt in geamortiseerde constante tijd per marked-content-reeks. Serialisatie is één lineaire doorgang over de set elementen. Bij HTML-gestuurde tagging zit de grootste kost in de HTML-pijplijn zelf, niet in de tag-emissie. De per-recipe-limiet die is vastgelegd in performance_budget (1500 ms wandkloktijd, 64 MB piek) geldt voor een typisch semantisch document met meerdere pagina’s. Grote documenten schalen lineair met het aantal elementen in plaats van met het aantal pagina’s.

Taaltags en toegankelijkheidsattributen stromen in PDF-name- en string-objecten. NextPDF escapet ze via PdfStringEscaper, zodat onjuist opgemaakte of vijandige waarden voor taal, alternatieve tekst, vervangende tekst en titel niet uit hun PDF-objectcontext kunnen breken. Strikte modus weigert ook niet-geregistreerde BCP 47-tags bij de API-grens, waardoor het invoeroppervlak smaller wordt voordat het de writer bereikt. Toegankelijkheidsattributen kunnen vrije tekst bevatten die door de auteur is aangeleverd. Behandel ze als niet-vertrouwde uitvoer en beoordeel ze zoals je andere documentinhoud beoordeelt. Zie de Conformance-module voor het gedrag van de profielcontroleur.

Deze pagina koppelt het gedrag van de bibliotheek aan clause-identifiers. Dat garandeert niet dat je uitvoer conform is. De geciteerde clauses zijn geparafraseerd, nooit letterlijk geciteerd. Zie de PDF/UA-2-specificatietoewijzing voor de tabel op provisieniveau en de expliciete niet-dekking. Hashes van citation-chunks worden vastgelegd in docs/public/modules/core/_normative-evidence-a11y.md.