PDFlibPas kan mærke et dokument mens det tegnes. Slå SetAutoTagMode til, og almindelige DrawText-kald bliver afsnit, tekst tegnet umiddelbart efter RegisterHeading bliver en overskrift på det niveau, løbende sidehoveder og sidefødder bliver artefakter som en læser springer over, billeder bliver figurer, og DrawTableRows bærer tabellen, dens rækker og dens celler ind i strukturtræet
Alternativet — og indtil for nylig den eneste mulighed — var at indkapsle hver tegneoperation i BeginTag og EndTag manuelt. Det virker, og for dokumenter med usædvanlig struktur er det stadig det rigtige værktøj. For den almindelige rapport, faktura eller opgørelse betyder det, at outputtets tilgængelighed afhænger af, at ingen nogensinde glemmer et par, på tværs af hver kodevej, der tegner noget som helst
Hvad dækker tilstandsbittene?
SetAutoTagMode tager en bit-maske og returnerer den tilstand, der tidligere var gældende. AUTOTAG_TEXT (1) mærker tekst som et afsnit eller som en overskrift, når en er ved at skulle til. AUTOTAG_FURNITURE (2) markerer løbende sidehoveder, sidefødder og sidenumre som artefakter. AUTOTAG_FIGURE (4) omdanner et tegnet billede til en figur eller til et artefakt, når det blev erklæret dekorativt. AUTOTAG_TABLE (8) bærer tegnede tabeller ind i strukturtræet. AUTOTAG_DEFAULT er 15, hvilket er alle fire
At slå tilstanden til markerer også dokumentet som tagged, og det skridt er mindre kosmetisk, end det lyder. En læser anser et dokument for at være utaggedt, medmindre kataloget siger andet (ISO 32000-1 §14.7.1), så en fil, der bærer et komplet strukturtræ uden nogen /MarkInfo-erklæring, bliver af hjælpemidler annonceret som slet ikke at have nogen struktur. Træet er der; intet læser det
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;
Hvordan ved en overskrift, hvilken tekst den tilhører?
RegisterHeading angiver niveauet for den næste tekst, der tegnes, og den venter på tekst. Hvis et billede tegnes imellem, bliver billedet til en figur, og overskriften forbliver afventende for den tekst, der følger. Den opførsel er bevidst: alternativet, hvor billedet tager overskriftsniveauet, producerede dokumenter, hvor en dekorativ streg under en titel blev annonceret som titlen
Den samme "opbrugt på ét element"-regel gælder for figurer. RegisterFigure leverer den beskrivelse, det næste billede bærer, og RegisterDecoration erklærer det næste billede som en streg, kant eller baggrund uden mening. Begge opbruges af ét billede, så et senere billede arver aldrig en beskrivelse, der var beregnet til et tidligere — hvilket er måden, alt-tekst ender på det forkerte billede i manuelt mærket kode
Beskrivelsen betyder mere end nogen anden enkelt streng i et tilgængeligt dokument. En ikke-seende læser får beskrivelsen i stedet for billedet, og det er det hele, de får. "Diagram" er ikke en beskrivelse; "Kvartalsvis omsætning efter region, med den østlige region højest i Q3" er
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
Tabeller, sidehoveder og hvor beslutningen om gentagelse bor
Med tabel-bittet slået til bærer DrawTableRows tabellen, dens rækker og dens celler ind i strukturtræet, så en læser kan sige, hvilken kolonne en værdi står i, frem for at læse hele tabellen som en række ubeslægtet tekst. SetTableHeaderRowCount angiver, hvor mange af de førende rækker der er sidehoveder; disse rækker skrives som header-celler med kolonne-scope, og det er netop det, der lader en læser annoncere overskriften på den værdi, brugeren står på
Header-rækker navngivet på denne måde bliver, hvor de er. At gentage dem i toppen af hver side er en layout-beslutning, og den forbliver en: DrawTaggedTableRows tager et RepeatHeaderRows-argument til netop det formål. At holde de to adskilde forhindrer strukturtræet i at få en anden kopi af headeren for hvert sideskift, hvilket er, hvad en automatisk gentagelse ville producere
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;
Blanding af automatisk og manuel mærkning
Automatisk mærkning træder til side inden i et tag, der er åbnet manuelt. Del af et dokument kan beskrives af din kode og resten overlades til biblioteket, uden at de to nesting ind i hinanden — hvilket er den ordning, de fleste virkelige dokumenter ønsker. Forsiden og signaturblokken har struktur, kun du forstår; de to hundrede sider brødtekst imellem gør ikke
To sikkerhedsregler holder output rent. Intet mærkes inden i et artefakt, fordi indhold, der markeres som et artefakt, ikke må bære noget struktur-element. Og tom tekst åbner intet element, så et løst DrawText med en tom streng ikke kan producere et struktur-element, som en læser ville annoncere som tomt. Begge er den slags fejl, som manuelt mærkede dokumenter akkumulerer stille, og som en validator rapporterer i bundget måneder senere
Hvad automatisk mærkning stadig ikke beslutter for dig
Læserækkefølge ud over tegnerækkefølge, semantiske roller der ikke er afsnit, overskrift, figur eller tabel, og sprog-erklæringer. Automatisk mærkning tildeler struktur i den rækkefølge, indhold tegnes — hvis din layout-kode tegner sidemargen før brødteksten, er det den rækkefølge, træet registrerer. For dokumenter, hvor visuel rækkefølge og læserækkefølge reelt afviger, forbliver den manuelle mærknings-API det rigtige værktøj, og gennemgangen af tagget PDF og tilgængelighedsstruktur dækker roller, scopes og header-bindinger i detaljer
Når dokumentet er færdigt, valider frem for at antage: noterne om PDF/A- og PDF/UA-preflight viser, hvordan man får en dom på den struktur, du har produceret, og gennemgangen af dataset-drevet rapport-eksport dækker, hvor disse kald passer ind i en rapport-motor, der genererer sit layout ud fra data
PDFlibPas er et Pascal-baseret PDF-bibliotek til Delphi, C++Builder og Lazarus uden ekstern PDF-runtime, så tilgængeligt output produceres af den samme kode, der tegner dokumentet — se PDFlibPas-produktsiden for den fulde API- og platform-liste