Ga naar inhoud

Een getagde PDF/UA-2-structuurboom genereren uit semantische inhoud

Met dit recipe maak je een getagd Portable Document Format/Universal Accessibility 2 (PDF/UA-2)-bestand. Het is gericht op International Organization for Standardization (ISO) 14289-2. NextPDF genereert een logische structuurboom, marked-content-reeksen, de catalogustaal en identificatiemetadata op documentniveau. Die structuur ondersteunt toegankelijk auteurschap, maar een onafhankelijke checker bepaalt de conformiteit. Het recipe volgt examples/31-pdfua2-tagged.php.

Terminal window
composer require nextpdf/core:^3

Zet een PDF/UA-2-checker op PATH voor de verificatie. Dit recipe gebruikt veraPDF met de ua2-variant. Je hebt geen Pro- of Enterprise-pakket nodig om de getagde structuur te genereren.

Een getagde PDF bevat naast de visuele content-stream een parallelle logische structuurboom. Hulptechnologie leest de boom in plaats van de pixel-lay-out, dus de structuur bepaalt de leesvolgorde die wordt aangeboden. ISO 14289-2 stelt hier vier eisen. Echte (niet-artefact) inhoud moet via die boom bereikbaar zijn (§8.2.2). Structuurelementen moeten volgens een gedefinieerde volgorde genest zijn (§8.2.3). Elk element moet, rechtstreeks of via role mapping, herleidbaar zijn tot een bekende structuurnaamruimte (§8.2.4). De natuurlijke taal van de inhoud wordt op documentniveau gedeclareerd en per structuurelement verfijnd waar deze afwijkt (§8.4.4).

NextPDF modelleert dit met een getypeerde ConformanceMode. enableTaggedPdf() stelt ConformanceMode::PdfUa2 in. Daardoor (a) koppelt de Hypertext Markup Language (HTML)-pijplijn een TaggedContentEmitter aan op het moment dat de parser wordt geconstrueerd, (b) stelt de catalogus-MarkInfoMarked-vlag in die een getagde PDF signaleert (ISO 32000-2 §14.7), en (c) legt de Best Current Practice 47 (BCP 47)-taal vast voor de catalogus-Lang-vermelding. De writer genereert ook de Tabs-vermelding per pagina, zodat de tabvolgorde de structuurvolgorde volgt (ISO 32000-2 §14.8).

De strikte UA-2-invarianten gelden alleen voor ConformanceMode::PdfUa2. Als je een strikte ConformancePolicy tegen een andere modus construeert, werpt dat bewust InvalidConfigException.

Het API-oppervlak (application programming interface) is afgeleid uit PHPDoc. Gebruik deze belangrijkste ingangspunten:

  • \NextPDF\Core\Document::createStandalone(): Document
  • Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): static
  • Document::setLanguage(string $lang): static
  • \NextPDF\Conformance\ConformancePolicy::strictUa2(): self
  • \NextPDF\Conformance\ConformanceMode::PdfUa2 (de modus die wordt ingesteld door enableTaggedPdf())
  • Document::beginTag(string $type): static / Document::endTag(): static (handmatige tagging voor niet-HTML-inhoud)
examples/31-pdfua2-tagged.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// Enable tagged mode BEFORE writeHtml(). The HTML pipeline detects the
// mode at parser construction time and wires the tagged-content emitter.
$doc->enableTaggedPdf(lang: 'en');
$doc->setTitle('Quarterly Accessibility Report');
$doc->setLanguage('en');
$doc->addPage();
$doc->writeHtml(<<<'HTML'
<h1>Quarterly Accessibility Report</h1>
<p>This document opts into tagged PDF so assistive technology can expose
a meaningful reading order.</p>
<ul>
<li>Headings carry semantic roles.</li>
<li>Lists keep their item structure.</li>
</ul>
HTML);
$doc->save(__DIR__ . '/output/31-pdfua2-tagged.pdf');
echo "Created: output/31-pdfua2-tagged.pdf\n";

Dit op zichzelf staande programma kan in de harness draaien. Laat de productieflow bij een misvormde taaltag snel falen, in plaats van dit pas te ontdekken wanneer de externe checker draait. Geef ConformancePolicy::strictUa2() mee om een ongeldige BCP 47-tag bij de API-grens af te wijzen, en koppel de build vervolgens aan het oordeel van de checker.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Conformance\ConformancePolicy;
use NextPDF\Core\Document;
use NextPDF\Exception\InvalidConfigException;
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: (__DIR__ . '/accessible.pdf');
try {
$doc = Document::createStandalone();
// Strict UA-2: a malformed BCP 47 tag throws here, not silently at
// write time. strictUa2() also forces the §8.4.4 Lang validation.
$doc->enableTaggedPdf(lang: 'en-GB', policy: ConformancePolicy::strictUa2());
$doc->setTitle('Accessible Annual Report 2026');
$doc->setLanguage('en-GB');
$doc->addPage();
$doc->writeHtml(<<<'HTML'
<h1>Annual Report 2026</h1>
<p>Audited results for the financial year ending March 2026.</p>
<h2>Segment performance</h2>
<table>
<tr><th>Segment</th><th>Revenue</th></tr>
<tr><td>Cloud</td><td>42.1</td></tr>
<tr><td>Services</td><td>18.7</td></tr>
</table>
HTML);
$doc->save($out);
} catch (InvalidConfigException $e) {
fwrite(STDERR, "Tagged PDF/UA-2 setup rejected: {$e->getMessage()}\n");
exit(1);
}
// The gate is the checker, not the library.
$exitCode = 0;
$report = [];
exec('verapdf --flavour ua2 ' . escapeshellarg($out), $report, $exitCode);
if ($exitCode !== 0) {
fwrite(STDERR, "veraPDF FAILED — output is not PDF/UA-2 conforming\n");
fwrite(STDERR, implode("\n", $report) . "\n");
exit(1);
}
echo "veraPDF PASS — accessible.pdf carries a conforming UA-2 structure\n";

Op een host waar verapdf --flavour ua2 een conform bestand meldt, is de verwachte standaarduitvoer (STDOUT):

veraPDF PASS — accessible.pdf carries a conforming UA-2 structure

Als enableTaggedPdf() de taaltag afwijst, eindigt het programma met een niet-nul-status na Tagged PDF/UA-2 setup rejected: … op de standaardfoutuitvoer (STDERR). Als de checker een probleem meldt, eindigt het programma met een niet-nul-status na veraPDF FAILED — output is not PDF/UA-2 conforming. De checker geeft het oordeel: NextPDF genereert de structuur, maar stelt geen conformiteit vast.

  • Aanroepvolgorde. enableTaggedPdf() na writeHtml() tagt al geschreven inhoud niet met terugwerkende kracht. Schakel de getagde modus eerst in.
  • Strikte taalpoort. Zonder een policy wordt een niet-parseerbare BCP 47-tag stilzwijgend verwijderd en komt die pas bij de checker aan het licht. Met ConformancePolicy::strictUa2() werpt dezelfde tag InvalidConfigException bij de enableTaggedPdf()-grens (ISO 14289-2 §8.4.4 strikt pad).
  • Idempotent opnieuw inschakelen. Als je enableTaggedPdf() tweemaal aanroept, werkt NextPDF de taal bij zonder een gevulde structuurboom opnieuw op te bouwen.
  • Handmatige tagging. Omhul items voor niet-HTML-inhoud met beginTag() / endTag(). Container-rollen (Table, TR, L, LI) worden groeperende elementen zonder marked content. Blad-rollen (P, H1H6, TD) krijgen marked-content-identifiers (MCID’s).
  • Modusexclusiviteit. Een strikte ConformancePolicy is alleen geldig met ConformanceMode::PdfUa2. Het combineren van strikte UA-2-vlaggen met een PDF/A-modus werpt InvalidConfigException. Maak een getagd PDF/A-product door de getagde modus en het PDF/A-profiel afzonderlijk in te schakelen.

De structuurboom voegt één parallelle boom van lichtgewicht woordenboeken toe en BDC/EMC-operatoren per tekstrun. Voor een typisch rapport bedraagt de overhead enkele procenten van de uitvoergrootte en blijft deze ruim binnen het budget van 2000 ms / 128 MB. Het semantische reproduceerbaarheidsprofiel is van toepassing, omdat een checkergericht product wordt vergeleken op basis van de structurele abstract syntax tree (AST) plus metadata, niet op basis van ruwe bytes. Zie de sectie Conformiteit.

De structuurboom bevat dezelfde tekst als de zichtbare inhoud. Als de bron-HTML persoonsgegevens bevat, waaronder persoonlijk identificeerbare informatie (PII), zijn die gegevens ook bereikbaar via de boom en via de ActualText/Alt-attributen. Pas vóór het auteurschap dezelfde redactie en minimalisatie toe als je voor de zichtbare inhoud zou doen. Tagging voegt geen nieuw exfiltratiepad toe, maar maakt de tekst bewust programmatisch extraheerbaar.

Het recipe schrijft alleen een vaste voortgangsregel naar STDOUT. Het routeert de PDF naar het zijkanaal van de harness (NEXTPDF_COOKBOOK_OUTPUT) of naar een pad dat de aanroeper opgeeft. Documenttekst wordt nooit gelogd. Houd checker-uitvoer, die inhoudsfragmenten kan weergeven, uit gedeelde logs.

Een getagde PDF is geen vertrouwensgrens. Als je afnemer de structuurboom voor geautomatiseerde verwerking vertrouwt, moet die het bestand nog steeds valideren, omdat een vijandige producent een structureel welgevormde maar misleidende boom kan genereren. Behandel de structuur als een toegankelijkheidsvoorziening, niet als een integriteits- of authenticiteitssignaal.

Dit recipe voert geen cryptografische bewerking uit. De Federal Information Processing Standards (FIPS)-modus verandert het gedrag ervan niet. Er is geen ondertekening of versleuteling bij betrokken.

PDF/UA-2-eisWat NextPDF genereertClausule
Echte inhoud bevindt zich in de structuurboomStructTreeRoot met StructElem per blok en MCID-gekoppelde marked contentISO 14289-2 §8.2.2
Gedefinieerde nesting en leesvolgordeBlokelementen toegewezen aan grouping/leaf-rollen in documentvolgordeISO 14289-2 §8.2.3
Bekende structuurnaamruimteRollen in de PDF 2.0-naamruimte; HTML-tags worden waar nodig via role mapping toegewezenISO 14289-2 §8.2.4
Document- en elementtaalCatalogus-Lang uit de BCP 47-tag; per-element-Lang wanneer deze afwijktISO 14289-2 §8.4.4
Niet-tekstuele inhoud heeft een tekstalternatiefAlt/ActualText op figure/non-text-structuurelementenISO 14289-2 §8.5.1
TabelrelatiesTable/TR/TH/TD-rollen met header-associatieISO 14289-2 §8.2.5.26
Identificatiemetadata van het onderdeelIdentificatie op documentniveau ingepland bij het opslaanISO 14289-2 §Intro (pdfua2#p17)

PDF/UA-2 legt toegankelijkheidseisen boven op de getagde-PDF-mechanismen van ISO 32000-2. NextPDF steunt op deze mapping:

NextPDF-emissieISO 32000-2 §14-voorzieningClausule
Logische structuurboom (StructTreeRoot)Getagde-PDF logische structuur§14.7 (iso32000_2_sec14#x1.x38.p13)
Catalogus-MarkInfo << /Marked true >>Getagde-PDF-markering§14.7 (iso32000_2_sec14#x1.x40.p3)
Tabs-vermelding per pagina die de structuurvolgorde volgtStructurele navigatie / tabvolgorde§14.8 (iso32000_2_sec14#x1.x50)

PDF/UA-2 is de PDF-formaatspecifieke uitwerking van structuureisen die Web Content Accessibility Guidelines (WCAG) 2.2 formaatonafhankelijk verwoordt. De relevante afstemming:

WCAG 2.2-succescriteriumPDF/UA-2-mechanisme dat dit recipe produceert
1.3.1 Info en relaties (niveau A)De structuurboom maakt koppen, lijsten en tabelrelaties programmatisch bepaalbaar (wcag_2_2#x2.x3.x3.x1.p3).
1.3.2 Betekenisvolle volgorde (niveau A)De structuurvolgorde bepaalt de leesvolgorde onafhankelijk van de visuele lay-out.
3.1.1 Taal van de pagina (niveau A)De catalogus-Lang-vermelding uit de BCP 47-tag.
1.1.1 Niet-tekstuele inhoud (niveau A)Alt/ActualText op niet-tekstuele structuurelementen (ISO 14289-2 §8.5.1).

Deze mapping laat zien waar de gegenereerde structuur een WCAG 2.2-criterium ondersteunt. Dit is geen WCAG-conformiteitsclaim. WCAG-conformiteit omvat de volledige gebruikerservaring, en een toegankelijkheidsevaluatie bepaalt die, niet de producent.

BeweringSpecificatieClausulereference_id
Echte inhoud vereist een logische structuur.ISO 14289-2§8.2.2
Structuurelementen volgen een gedefinieerde nesting en leesvolgorde.ISO 14289-2§8.2.3
Elk structuurelement is, rechtstreeks of via role mapping, herleidbaar tot een bekende naamruimte.ISO 14289-2§8.2.4
Natuurlijke taal wordt op document- en structuurelementniveau gedeclareerd.ISO 14289-2§8.4.4
Niet-tekstuele inhoud draagt een tekstalternatief.ISO 14289-2§8.5.1
Tabelcellen dragen row/header/data-relaties.ISO 14289-2§8.2.5.26
De getagde-PDF-markering is de catalogus-MarkInfoMarked-vlag.ISO 32000-2§14.7
Conformiteit wordt bepaald ten opzichte van het onderdeel, niet beweerd door de producent.ISO 14289-2§8.14.2

NextPDF genereert de getagde structuur die toegankelijk auteurschap ondersteunt. Ondersteuning is geen conformiteit. Dit recipe beweert geen PDF/UA-2-conformiteit. Een onafhankelijke checker, zoals veraPDF, doet die bepaling. Draai de checker voordat je stelt dat een bestand conform is.