Tehnički članak

Automatsko označavanje strukture za pristupačan PDF u Delphi

PDFlibPas može da označi dokument dok se crta. Uključite SetAutoTagMode i obični pozivi DrawText postaju pasusi, tekst nacrtan odmah posle RegisterHeading postaje naslov tog nivoa, tekući zaglavlja i podnožja postaju artefakti koje čitač preskače, slike postaju figure, a DrawTableRows nosi tabelu, njene redove i ćelije u stablo strukture

Alternativa — a do skoro jedina opcija — bila je ručno obavijanje svakog poziva za crtanje u BeginTag i EndTag. To radi, i za dokumente sa neobičnom strukturom i dalje je pravi alat. Za običan izveštaj, fakturu ili izvod, to znači da pristupačnost izlaza zavisi od toga da niko nikada ne zaboravi par, kroz svaku putanju koda koja išta crta

Šta pokrivaju bitovi moda

SetAutoTagMode prima bit-masku i vraća mod koji je prethodno važio. AUTOTAG_TEXT (1) označava tekst kao pasus, ili kao naslov kad je on na redu. AUTOTAG_FURNITURE (2) označava tekuća zaglavlja, podnožja i brojeve stranica kao artefakte. AUTOTAG_FIGURE (4) pretvara nacrtanu sliku u figuru, ili u artefakt kad je proglašena dekorativnom. AUTOTAG_TABLE (8) nosi nacrtane tabele u stablo strukture. AUTOTAG_DEFAULT je 15, što je sve četiri

Uključivanje moda takođe označava dokument kao označen, i taj korak je manje kozmetički nego što zvuči. Čitač smatra dokument neoznačenim osim ako katalog ne kaže drugačije (ISO 32000-1 §14.7.1), pa fajl koji nosi kompletno stablo strukture bez /MarkInfo deklaracije asistivna tehnologija najavljuje kao da uopšte nema strukturu. Stablo je tu; ništa ga ne čita

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;

Otkud naslov zna kom tekstu pripada?

RegisterHeading imenuje nivo za sledeći nacrtani tekst, i čeka na tekst. Ako se između to nacrta slika, slika postaje figura, a naslov ostaje na čekanju za tekst koji sledi. To ponašanje je namerno: alternativa, gde slika uzima nivo naslova, proizvodila je dokumente u kojima je dekorativna linija ispod naslova bila najavljivana kao naslov

Isto pravilo «potroši se na jednu stavku» upravlja figurama. RegisterFigure daje opis koji sledeća slika nosi, a RegisterDecoration proglašava sledeću sliku linijom, ivicom ili pozadinom koja ne nosi značenje. Oba se troše na jednu sliku, tako da kasnija slika nikada ne nasleđuje opis namenjen ranijoj — što je način na koji alt tekst završi pridružen pogrešnoj slici u ručno označenom kodu

Opis je važniji od bilo koje druge pojedinačne niske u pristupačnom dokumentu. Čitalac bez vida dobija opis na mestu slike, i to je sve što dobija. «Grafikon» nije opis; «Kvartalni prihod po regionu, sa istočnim regionom najvišim u Q3» jeste

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

Tabele, zaglavlja i gde živi odluka o ponavljanju

Sa uključenim bitom za tabele, DrawTableRows nosi tabelu, njene redove i ćelije u stablo strukture, tako da čitač može reći u kojoj se koloni nalazi vrednost umesto da čita celu tabelu kao niz nepovezanog teksta. SetTableHeaderRowCount imenuje koliko vodećih redova su zaglavlja; ti redovi se pišu kao ćelije zaglavlja koje nose opseg kolone, što je upravo ono što omogućava čitaču da najavi naslov vrednosti na kojoj se korisnik nalazi

Redovi zaglavlja imenovani na ovaj način ostaju gde jesu. Ponavljanje ih na vrhu svake stranice je odluka rasporeda, i takva i ostaje: DrawTaggedTableRows uzima argument RepeatHeaderRows upravo za tu svrhu. Držanje ovo dvoje odvojenim izbegava da stablo strukture stekne drugu kopiju zaglavlja za svaki prelom stranice, što bi automatsko ponavljanje proizvelo

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;

Mešanje automatskog i ručnog označavanja

Automatsko označavanje staje sa strane unutar oznake otvorene ručno. Deo dokumenta može opisati vaš kod, a ostatak prepustiti biblioteci, bez da se ovo dvoje međusobno gnjezde — što je raspored koji većina realnih dokumenata želi. Naslovna stranica i blok za potpis imaju strukturu koju samo vi razumete; dve stotine stranica telo teksta između nemaju

Dva bezbednosna pravila drže izlaz čistim. Ništa se ne označava unutar artefakta, jer sadržaj označen kao artefakt ne sme nositi nijedan element strukture. I prazan tekst ne otvara nijedan element, tako da lutajući DrawText sa praznom niskom ne može proizvesti element strukture koji bi čitač najavio kao prazan. Oba su vrsta defekta koje ručno označeni dokumenti tiho akumuliraju, a validator prijavljuje masovno mesecima kasnije

Šta automatsko označavanje još uvek ne odlučuje za vas

Redosled čitanja van redosleda crtanja, semantičke uloge koje nisu pasus, naslov, figura ili tabela, i deklaracije jezika. Automatsko označavanje dodeljuje strukturu redosledom kojim se sadržaj crta — ako vaš kod rasporeda crta bočnu traku pre tela, to je redosled koji stablo beleži. Za dokumente gde vizuelni redosled i redosled čitanja se iskreno razlikuju, API za ručno označavanje ostaje pravi alat, a prolaz kroz označeni PDF i strukturu pristupačnosti pokriva uloge, opsege i vezivanja zaglavlja detaljno

Kad je dokument završen, validirajte umesto da pretpostavljate: beleške o PDF/A i PDF/UA preflightu pokazuju kako dobiti verdikt o strukturi koju ste proizveli, a prolaz kroz izvoz izveštaja vođen skupom podataka pokriva gde se ovi pozivi uklapaju u mašinu za izveštaje koja generiše raspored iz podataka

PDFlibPas je nativna Pascal PDF biblioteka za Delphi, C++Builder i Lazarus bez eksternog PDF izvršnog okruženja, tako da pristupačan izlaz proizvodi isti kod koji crta dokument — pogledajte PDFlibPas stranicu proizvoda za kompletan API i listu platformi