PDFlibPas kan een document taggen terwijl het wordt getekend. Zet SetAutoTagMode aan en gewone DrawText-aanroepen worden alinea's, tekst die direct na RegisterHeading wordt getekend wordt een kop op dat niveau, lopende kop- en voetteksten worden artifacts die een reader overslaat, afbeeldingen worden figures, en DrawTableRows brengt de tabel, zijn rijen en zijn cellen over naar de structuurboom
Het alternatief — en tot voor kort de enige optie — was elke tekens-aanroep met de hand in BeginTag en EndTag wikkelen. Dat werkt, en voor documenten met ongebruikelijke structuur is het nog steeds het juiste gereedschap. Voor het gewone rapport, de factuur of het afschrift betekent het dat de toegankelijkheid van de uitvoer afhangt van het feit dat niemand ooit een paar vergeet, langs elke codepath die iets tekent
Wat de mode-bits dekken
SetAutoTagMode neemt een bit-masker en retourneert de mode die daarvoor gold. AUTOTAG_TEXT (1) tagt tekst als een alinea, of als een kop wanneer er een openstaat. AUTOTAG_FURNITURE (2) markeert lopende kop- en voetteksten en paginanummers als artifacts. AUTOTAG_FIGURE (4) maakt van een getekende afbeelding een figure, of een artifact wanneer deze als decoratief was aangemerkt. AUTOTAG_TABLE (8) brengt getekende tabellen over naar de structuurboom. AUTOTAG_DEFAULT is 15, alle vier dus
Het aanzetten van de mode markeert het document ook als tagged, en die stap is minder cosmetisch dan hij klinkt. Een reader beschouwt een document als untagged tenzij de catalogus anders zegt (ISO 32000-1 §14.7.1), dus een bestand dat een complete structuurboom meedraagt zonder /MarkInfo-declaratie wordt door hulptechnologie aangekondigd als document zonder enige structuur. De boom is aanwezig; niets leest hem
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.SetAutoTagMode(AUTOTAG_DEFAULT); // text + furniture + figures + tables
Lib.AddStandardFont(4);
Lib.SetTextSize(18);
Lib.RegisterHeading(1, 'Annual service report');
Lib.DrawText(72, 96, 'Annual service report'); // becomes H1
Lib.SetTextSize(11);
Lib.DrawText(72, 130, 'Every unit installed before 2024 was inspected.');
Lib.SaveToFile('report.pdf');
finally
Lib.Free;
end;
end;
Hoe weet een kop bij welke tekst hij hoort?
RegisterHeading benoemt het niveau voor de volgende tekst die wordt getekend, en het wacht op tekst. Wordt ertussen een afbeelding getekend, dan wordt die afbeelding een figure en blijft de kop in behandeling voor de tekst die volgt. Dat gedrag is bewust gekozen: het alternatief, waarbij de afbeelding het kopniveau overneemt, produceerde documenten waarin een decoratieve lijn onder een titel als de titel werd aangekondigd
Dezelfde regel "verbruikt door één item" geldt voor figures. RegisterFigure levert de beschrijving die de volgende afbeelding meedraagt, en RegisterDecoration verklaart de volgende afbeelding tot een lijn, rand of achtergrond zonder betekenis. Beide worden door precies één afbeelding verbruikt, zodat een latere afbeelding nooit een beschrijving erft die voor een eerdere was bedoeld — precies hoe alt-tekst in hand-getagd code aan het verkeerde plaatje blijft hangen
De beschrijving telt zwaarder dan elke andere tekenreeks in een toegankelijk document. Een blinde of slechtziende lezer krijgt de beschrijving in plaats van de afbeelding, en dat is alles wat hij krijgt. "Diagram" is geen beschrijving; "Kwartaalomzet per regio, met de oostelijke regio het hoogst in Q3" is dat wel
Lib.RegisterFigure('Exploded view of the gearbox assembly');
Lib.AddImageFromFile('gearbox.png', 0); // becomes a tagged Figure
Lib.RegisterDecoration; // meaningless rule
Lib.AddImageFromFile('divider.png', 0); // drawn inside a layout artifact
Tabellen, kopteksten en waar de herhalingsbeslissing thuishoort
Met de tabel-bit aan brengt DrawTableRows de tabel, zijn rijen en zijn cellen over naar de structuurboom, zodat een reader kan zeggen in welke kolom een waarde staat in plaats van de hele tabel voor te lezen als een reeks losse tekens. SetTableHeaderRowCount benoemt hoeveel van de leidende rijen kopteksten zijn; die rijen worden geschreven als kopcellen met een kolom-scope, wat precies is wat een reader in staat stelt de koptekst aan te kondigen van de waarde waar de gebruiker zich bevindt
Koprijen die zo worden benoemd, blijven waar ze zijn. Ze bovenaan elke pagina herhalen is een lay-outbeslissing, en dat blijft het ook: DrawTaggedTableRows heeft een RepeatHeaderRows-argument voor precies dat doel. De twee gescheiden houden voorkomt dat de structuurboom bij elke pagina-einde een tweede kop meekrijgt, wat een automatische herhaling zou opleveren
var
TableID: Integer;
begin
TableID := Lib.CreateTable(40, 3);
Lib.SetTableHeaderRowCount(TableID, 1); // row 1 is the header band
Lib.SetTableCellContent(TableID, 1, 1, 'Part');
Lib.SetTableCellContent(TableID, 1, 2, 'Torque');
Lib.SetTableCellContent(TableID, 1, 3, 'Unit');
// ... fill the data rows ...
// Draw rows 1..40 into a 600pt band, repeating one header row per page
Lib.DrawTaggedTableRows(TableID, 72, 150, 600, 1, 40, 1);
end;
Automatische en handmatige tagging combineren
Automatisch taggen wijkt binnen een met de hand geopende tag. Een deel van een document kan door jouw code worden beschreven en de rest aan de bibliotheek worden overgelaten, zonder dat de twee in elkaar nesten — wat de arrangement is die de meeste echte documenten willen. De omslagpagina en het handtekeningblok hebben structuur die alleen jij begrijpt; de tweehonderd pagina's body-tekst ertussen niet
Twee veiligheidsregels houden de uitvoer schoon. Binnen een artifact wordt niets getagd, omdat content die als artifact is gemarkeerd geen structuurelement mag meedragen. En lege tekst opent geen element, dus een verdwaalde DrawText met een lege tekenreeks kan geen structuurelement produceren dat een reader als leeg zou aankondigen. Beide zijn het soort defecten dat hand-getagde documenten stilzwijgend opstapelen en dat een validator maanden later in bulk rapporteert
Wat automatisch taggen nog steeds niet voor je beslist
Leesvolgorde voorbij de tekenvolgorde, semantische rollen die geen alinea, kop, figure of tabel zijn, en taaldeclaraties. Automatisch taggen wijst structuur toe in de volgorde waarin content wordt getekend — als je lay-outcode de zijbalk vóór de body tekent, is dat de volgorde die de boom vastlegt. Voor documenten waar visuele volgorde en leesvolgorde werkelijk verschillen, blijft de handmatige tagging-API het juiste gereedschap, en de doorloop van tagged PDF en toegankelijkheidsstructuur behandelt rollen, scopes en kopbindingen in detail
Bij afronding van het document: valideer in plaats van aan te nemen. De notities over PDF/A- en PDF/UA-preflight laten zien hoe je een uitspraak krijgt over de structuur die je hebt geproduceerd, en de doorloop van datasetgestuurde rapportexport behandelt waar deze aanroepen passen in een rapportengine die zijn lay-out uit gegevens genereert
PDFlibPas is een native Pascal PDF-bibliotheek voor Delphi, C++Builder en Lazarus zonder externe PDF-runtime, zodat toegankelijke uitvoer wordt geproduceerd door dezelfde code die het document tekent — zie de PDFlibPas-productpagina voor de volledige API en platformlijst