Pristupačan PDF se oslanja na strukturu koju vidljiva stranica nikada ne prikazuje: stablo strukture definisano u ISO 32000-1 §14.7. To je logička hijerarhija naslova, pasusa, tabela i slika, postavljena preko nacrtanog sadržaja i mapirana na standardne uloge pomoću mape uloga (role map). Čitač ekrana čita to stablo, a ne oznake na stranici. Bez njega, generisana faktura koja izgleda besprekorno je semantički prazna, jer tok sadržaja beleži samo redosled crtanja i ništa više. Ukupan iznos može biti saopšten pre pojedinačnih stavki, podnožje (footer) može prekinuti pasus, a tabela stavki se može spojiti u jedan neprepoznatljiv niz reči. Cena sprečavanja toga je nesrazmerno na vašoj strani. Generisanje strukture tokom crtanja zahteva svega nekoliko minuta pisanja koda, dok je njeno naknadno dodavanje u gotove dokumente ogroman projekat sanacije. losLab PDF Library (PDFlibPas) izlaže stablo strukture Delphi-ju i C++Builder-u kroz mali skup poziva koji umotavaju svaku operaciju crtanja u njenu logičku ulogu.
Kako se označeni sadržaj povezuje sa stablom strukture
Dva sloja sarađuju. U toku sadržaja, operacije crtanja su grupisane u sekvence označenog sadržaja (marked-content), od kojih svaka nosi celobrojni MCID. U katalogu dokumenta, stablo strukture mapira te MCID-ove u hijerarhiju tipiziranih elemenata (H1, P, Table, Figure) sa atributima kao što su alternativni tekst i jezik. Prilagođeni tipovi elemenata su dozvoljeni, ali svaki od njih se mora razrešiti u standardnu ulogu preko mape uloga (ISO 32000-1 §14.8.4). Sadržaj koji nema nikakvo značenje, poput linija, pozadina i ponavljajućih elemenata stranice, označava se kao artefakt (artifact) kako bi ga asistivne tehnologije preskočile umesto da ga čitaju usred rečenice.
PDFlibPas održava oba sloja iza jednog para zagrada. BeginTag otvara element strukture i započinje sekvencu označenog sadržaja, pozivi za crtanje se smeštaju unutar njega, a EndTag zatvara oba. Evidencija koja često otežava ručno tagovanje, kao što su MCID-ovi, roditeljsko stablo i reference stranica, odvija se interno gde je ne možete pogrešiti.
Dva prekidača na nivou dokumenta definišu rad pre otvaranja bilo kog taga. SetMarkInfo upisuje zastavicu u katalog koja deklariše da je dokument tagovan, a IsTaggedPDF je čita nazad, što je brz prvi test pri odlučivanju da li ulazni fajl ima strukturu koju vredi sačuvati. Jezik ima dve ulazne tačke. SetDocumentLanguage samostalno postavlja podrazumevani jezik dokumenta, dok ga SetPDFUAMode postavlja u sklopu omogućavanja potpunog PDF/UA izlaza. Fajl može biti korisno tagovan i bez tvrdnje o usklađenosti sa PDF/UA standardom, a postepeno uvođenje često počinje upravo odatle.
Tagovanje tokom crtanja, a ne naknadno
Uspešan šablon generisanja jeste tretiranje zagrade taga kao dela potpisa svakog poziva za crtanje, a nikako kao naknadnog koraka:
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1); // top-left origin
Lib.SetPDFUAMode('en-US'); // bumps the save version to PDF 1.7
Lib.SetInformation(1, 'Service Manual'); // /Title is mandatory for PDF/UA
Lib.AddRoleMap('ManualTitle', 'H1'); // custom type -> standard role
Lib.AddStandardFont(4);
Lib.SetTextSize(18);
Lib.BeginTagEx2('ManualTitle', '', '', 'en-US', '', 'h1-cover', '');
Lib.DrawText(72, 96, 'Service Manual');
Lib.EndTag;
Lib.BeginTag('Figure', 'Exploded view of the gearbox assembly', '');
Lib.AddImageFromFile('gearbox.png', 0);
Lib.EndTag;
Lib.BeginArtifact('Layout'); // page decoration: excluded from reading
// ... draw rules and background tint ...
Lib.EndArtifact;
Lib.SaveToFile('manual.pdf');
finally
Lib.Free;
end;
end;
Tri poziva u toj sekvenci nose težinu usklađenosti. SetPDFUAMode omogućava PDF/UA izlaz i tiho povećava verziju dokumenta na PDF 1.7, što se kosi sa fiksiranjem verzije. Dokument zaključan na PDF 1.4 pomoću LockSaveVersion odbija da se sačuva i vraća šifru greške 602 kada je UA režim aktivan, što je konflikt koji obično isplivava kada različiti timovi konfigurišu arhivske profile i zahteve pristupačnosti. SetInformation(1, ...) upisuje naslov dokumenta, za koji ISO 14289 očekuje da ga pregledači prikažu umesto imena fajla; njegov izostanak je jedan od najčešćih nalaza neusklađenosti sa PDF/UA u praksi. AddRoleMap registruje prilagođeni tip ManualTitle kao H1, a njegovo preskakanje dovodi do toga da dijagnostika opisana u nastavku označi nemapiranu ulogu.
Nivoi naslova zahtevaju promišljeno pravilo, a ne ad-hoc izbore napravljene radi izgleda stranice. Korisnici čitača ekrana skaču sa sekcije na sekciju pomoću prečica za naslove, pa šablon koji ide sa H1 direktno na H3 jer je srednji nivo izgledao prevelik u vizuelnom dizajnu tiho narušava tu navigaciju, a nijedan vizuelni pregled to neće uočiti. To je upravo propust koji dijagnostika HEADING-LEVEL-SKIP prepoznaje. Mapirajte vizuelne stilove svakog šablona na fiksnu strukturu naslova jednom, na jednom mestu, i odstupanja se nikada neće pojaviti.
Tabele prema kojima čitač ekrana zapravo može navigirati
Nacrtane linije mreže ne znače ništa van ekrana. Čitači ekrana navigiraju kroz strukturne odnose: koje ćelije su zaglavlja, čime svako zaglavlje upravlja i kako se ćelije sa podacima povezuju sa zaglavljima u nepravilnim rasporedima. Pozivi atributa elemenata strukture upravljaju svim ovim parametrima:
Lib.BeginTag('Table', '', '');
Lib.BeginTag('TR', '', '');
Lib.BeginTagEx2('TH', '', '', '', '', 'col-part', '');
Lib.SetStructElemScope('Column'); // valid only while this TH is open
Lib.DrawText(72, 120, 'Part');
Lib.EndTag;
Lib.BeginTagEx2('TH', '', '', '', '', 'col-torque', '');
Lib.SetStructElemScope('Column');
Lib.SetStructElemColSpan(2); // header spans the value and unit columns
Lib.DrawText(200, 120, 'Tightening torque');
Lib.EndTag;
Lib.EndTag;
Lib.BeginTag('TR', '', '');
Lib.BeginTag('TD', '', '');
Lib.SetStructElemHeaders('col-part'); // explicit binding for irregular tables
Lib.DrawText(72, 140, 'M8 flange bolt');
Lib.EndTag;
Lib.EndTag;
Lib.EndTag; // Table
Pravilo redosleda je strogo i izvršava se bez prijavljivanja grešaka. Svaki poziv SetStructElem* odnosi se na tag koji je otvoren u tom trenutku, između njegovih BeginTag i EndTag poziva, i vraća 0 bez podizanja izuzetka ako nijedan tag nije otvoren ili ako se atribut ne odnosi na trenutni tag. Pogrešno postavljen poziv jednostavno nestaje. Umotavanje povratnih vrednosti u tvrdnje (assertions) tokom razvoja omogućava vam da uočite odstupanje dok je još vidljivo; u suprotnom, nedostatak opsega (scope) se primećuje tek kada revizija pristupačnosti pokrene pravi čitač ekrana preko tabele. ID-jevi elemenata prosleđeni kroz BeginTagEx2 pune stablo ID-jeva (ISO 32000-1 §14.7.4), i to je ono što uopšte omogućava razrešavanje povezivanja funkcije SetStructElemHeaders.
Ista familija atributa pokriva i ostatak onoga na šta se asistivna tehnologija oslanja. SetStructElemListNumbering deklariše kako su stavke liste označene, tako da čitač ekrana najavljuje poziciju unutar liste umesto da izgovara simbole za nabrajanje. SetStructElemBBox beleži granični okvir (bounding box) slika i tabela, što prikazi sa prilagodljivim rasporedom (reflow) koriste za postavljanje sadržaja. SetStructElemActualText obezbeđuje zamenski tekst za delove čiji se glifovi ne mapiraju u čitljive karaktere, poput ukrasnog početnog slova (drop cap) sastavljenog od vektorske grafike. Svaki od njih sledi isto pravilo: povezuje se sa otvorenim tagom, ili nestaje.
Artefakti, jezik i dijagnostika pre čuvanja
Pnavljajući elementi stranice, odnosno zaglavlja stranica, oznake savijanja, vodeni žigovi i tonirane pozadine, pripadaju zagradama BeginArtifact i EndArtifact kako nikada ne bi ušli u tok čitanja. Jezik se nasleđuje. Podrazumevani jezik dokumenta dolazi iz argumenta funkcije SetPDFUAMode, a odlomak na drugom jeziku ga nadjačava po pojedinačnom elementu preko BeginTagEx ili SetStructElemLang. To je ono što omogućava da francuski citat unutar priručnika na engleskom jeziku bude pravilno izgovoren.
Pre čuvanja, GetPDFUADiagnostics pokreće strukturne provere biblioteke nad dokumentom u memoriji i vraća nalaze kao tekst, gde prazan string znači da ništa nije pronađeno. Kodovi direktno ukazuju na klasične autorske greške: FIGURE-NO-ALT za sliku bez alternativnog teksta, HEADING-LEVEL-SKIP za H3 koji prati H1, ROLEMAP-UNMAPPED za prilagođeni tip koji nikada nije registrovan. Povežite ovo sa procesom bildovanja (generišite skup dokumenata, prekinite korak ako dijagnostika nije prazna) i regresije pristupačnosti postaju neuspesi u fazi kompajliranja umesto nalaza revizije mesecima kasnije. Konačna presuda o usklađenosti i dalje pripada preflight proveri nad sačuvanim fajlom, što je pokriveno u članku o PDF/A i PDF/UA preflight proveri u Delphi-ju, jer se neke normalizacije primenjuju tek tokom serijalizacije.
Navigacija kroz anotacije ima sopstvenu kontrolu. PDF/UA standard očekuje da kretanje tastaturom kroz polja formulara i linkove prati redosled strukture, pa SetTabOrderMode upisuje unos za redosled tabulatora na nivou stranice koji pregledači poštuju, dok je GetTabOrderMode dostupan za proveru ulaznih fajlova. To je vrsta zahteva koju niko ne primećuje sve dok korisnik koji koristi isključivo tastaturu ne prijava bag, a za njegovo pravilno postavljanje potreban je samo jedan poziv po dokumentu.
Stabla strukture ne preživljavaju svako spajanje
Tagovani dokumenti ostaju tagovani samo kada svaki kasniji korak obrade čuva stablo strukture, a oštra ivica unutar PDFlibPas-a je familija funkcija za spajanje listi. MergeFileListFast žrtvuje očuvanje stabla strukture zarad brzine. To je dobar kompromis za serije skeniranih slika, ali pogrešan za tagovane izveštaje, jer se izlazni dokument otvara bez problema, renderuje identično, ali je tiho izgubio svoj sloj pristupačnosti. Koristite podrazumevanu funkciju MergeFileList ili strogu varijantu kad god je neki od ulaznih dokumenata tagovan, i neka IsTaggedPDF bude deo provera nakon sklapanja kako se spojen paket bez strukture ne bi isporučio a da to niko ne primeti. Cevovodi za sklapanje velikih skupova dokumenata nose više ovakvih kompromisa, koji su istraženi u članku o spajanju, deljenju i direktnom pristupu velikim PDF fajlovima.
Usklađivanje i referenca
Krug verifikacije se zatvara van biblioteke: otvorite izlazni dokument u programu Acrobat, pregledajte panel sa tagovima i pročitajte bar jedan dokument po familiji šablona pomoću pravog čitača ekrana. Dijagnostika otkriva strukturne greške; samo ljudsko uho može da uoči redosled čitanja koji je tehnički ispravan, ali u praksi zbunjujući. Evaluacione verzije i kompletna referenca API-ja za tagovanje nalaze se na stranici proizvoda losLab PDF Library for Delphi.