Pristupačan PDF počiva na jednoj strukturi koju vidljiva stranica nikada ne pokazuje: na stablu strukture definisanom u ISO 32000-1 §14.7. To je logička hijerarhija naslova, pasusa, tabela i slika, položena preko iscrtanog sadržaja i preslikana u standardne uloge kroz mapu uloga. Čitač ekrana čita to stablo, a ne tragove na stranici. Bez njega je besprekorno izgledajuća generisana faktura semantički prazna, jer tok sadržaja beleži samo redosled crtanja i ništa više. Ukupan iznos može biti izgovoren pre stavki, podnožje može da preseče pasus, a tabela stavki da se sruči u jedan nerazlučen niz reči. Cena sprečavanja svega toga naginje u vašu korist. Emitovanje strukture dok crtate su minuti koda; naknadno ugrađivanje u gotove dokumente je projekat sanacije. losLab PDF Library (PDF Library for Delphi) izlaže to stablo Delphiju i C++Builderu kroz mali skup poziva koji svaku operaciju crtanja obavijaju njenom logičkom ulogom
Kako se označeni sadržaj vezuje za stablo strukture
Sarađuju dva sloja. U toku sadržaja, operacije crtanja se uokviruju u nizove označenog sadržaja, od kojih svaki nosi celobrojni MCID. U katalogu dokumenta, stablo strukture preslikava te MCID vrednosti u hijerarhiju tipiziranih elemenata (H1, P, Table, Figure) sa atributima poput alternativnog teksta i jezika. Prilagođeni tipovi elemenata su dozvoljeni, ali se svaki mora razrešiti u standardnu ulogu kroz mapu uloga (ISO 32000-1 §14.8.4). Sadržaj koji uopšte ne nosi značenje, poput linija, pozadina i ponovljenog nameštaja stranice, označava se kao artefakt da bi ga pomoćna tehnologija preskočila umesto da ga čita usred rečenice
PDF Library for Delphi održava oba sloja iza jednog para zagrada. BeginTag otvara element strukture i započinje niz označenog sadržaja, pozivi crtanja padaju unutra, a EndTag zatvara oboje. Knjigovodstvo koje ruši ručno pisano tagovanje, dakle MCID vrednosti, stablo roditelja i reference na stranice, odvija se interno gde ne možete da pogrešite
Dva prekidača na nivou dokumenta uokviruju posao pre nego što se otvori ijedan tag. SetMarkInfo upisuje zastavicu u katalogu koja dokument proglašava tagovanim, a IsTaggedPDF je čita nazad, što je jeftina prva proba kada odlučujete da li dolazni fajl uopšte ima strukturu vrednu očuvanja. Jezik ima dve ulazne tačke. SetDocumentLanguage sam postavlja podrazumevani jezik dokumenta, dok ga SetPDFUAMode postavlja kao deo uključivanja punog PDF/UA izlaza. Fajl može biti korisno tagovan i bez pretendovanja na PDF/UA usaglašenost, a postepeno uvođenje često počinje upravo tu
Tagovanje tokom crtanja, a ne posle njega
Obrazac generisanja koji funkcioniše jeste da zagrade taga tretirate kao deo potpisa svakog poziva za crtanje, nikada kao kasniji prolaz:
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1); // koordinatni početak gore levo
Lib.SetPDFUAMode('en-US'); // podiže verziju čuvanja na PDF 1.7
Lib.SetInformation(1, 'Service Manual'); // /Title je obavezan za PDF/UA
Lib.AddRoleMap('ManualTitle', 'H1'); // prilagođeni tip -> standardna uloga
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'); // ukras stranice: izvan čitanja
// ... crtanje linija i pozadinske nijanse ...
Lib.EndArtifact;
Lib.SaveToFile('manual.pdf');
finally
Lib.Free;
end;
end;
Tri poziva u tom nizu nose težinu usaglašenosti. SetPDFUAMode uključuje PDF/UA izlaz i nečujno podiže verziju dokumenta na PDF 1.7, što se sudara sa zaključavanjem verzije. Dokument zaključan na PDF 1.4 pozivom LockSaveVersion odbija da se sačuva i vraća kod greške 602 čim je UA režim aktivan, a taj sudar obično izbije na površinu kada arhivske profile i zahteve pristupačnosti podešavaju različiti timovi. SetInformation(1, ...) upisuje naslov dokumenta, koji ISO 14289 očekuje da pregledači prikažu umesto imena fajla; njegovo odsustvo je jedan od najčešćih PDF/UA nalaza u praksi. AddRoleMap registruje prilagođeni tip ManualTitle kao H1, a preskakanje tog poziva ostavlja dijagnostiku opisanu niže da prijavljuje nemapiranu ulogu
Nivoi naslova zaslužuju promišljenu politiku, a ne odluke donete usput zbog izgleda stranice. Korisnici čitača ekrana skaču između odeljaka prečicom za naslove, pa šablon koji ide od H1 do H3 zato što je međunivo izgledao prevelik u vizuelnom dizajnu tiho kvari tu navigaciju, a nijedan vizuelni pregled to nikada neće uhvatiti. To je tačno onaj kvar koji dijagnostika HEADING-LEVEL-SKIP postoji da imenuje. Preslikajte vizuelne stilove svakog šablona u fiksne merdevine naslova jednom, na jednom mestu, i odstupanje nikada neće ni krenuti
Tabele kroz koje čitač ekrana zaista može da se kreće
Iscrtane linije mreže ne znače ništa van ekrana. Ono kroz šta se čitači ekrana kreću jesu strukturni odnosi: koje ćelije su zaglavlja, čime svako zaglavlje upravlja i kako se ćelije podataka vezuju za zaglavlja u nepravilnim rasporedima. Pozivi za atribute elemenata strukture pokrivaju sve troje:
Lib.BeginTag('Table', '', '');
Lib.BeginTag('TR', '', '');
Lib.BeginTagEx2('TH', '', '', '', '', 'col-part', '');
Lib.SetStructElemScope('Column'); // važi samo dok je ovaj TH otvoren
Lib.DrawText(72, 120, 'Part');
Lib.EndTag;
Lib.BeginTagEx2('TH', '', '', '', '', 'col-torque', '');
Lib.SetStructElemScope('Column');
Lib.SetStructElemColSpan(2); // zaglavlje pokriva kolone vrednosti i jedinice
Lib.DrawText(200, 120, 'Tightening torque');
Lib.EndTag;
Lib.EndTag;
Lib.BeginTag('TR', '', '');
Lib.BeginTag('TD', '', '');
Lib.SetStructElemHeaders('col-part'); // eksplicitno vezivanje za nepravilne tabele
Lib.DrawText(72, 140, 'M8 flange bolt');
Lib.EndTag;
Lib.EndTag;
Lib.EndTag; // Tabela
Pravilo redosleda je strogo i sprovodi se nečujno. Svaki poziv SetStructElem* primenjuje se na tag koji je u tom trenutku otvoren, između njegovog BeginTag i njegovog EndTag, i vraća 0 bez ijedne uzbune kada nijedan tag nije otvoren ili se atribut ne odnosi na trenutni. Pogrešno smešten poziv prosto ispari. Umotavanje povratnih vrednosti u tvrdnje tokom razvoja hvata odstupanje dok još možete da ga vidite; ostavljeno samo sebi, izostalo područje zaglavlja pokaže se tek kada revizija pristupačnosti pusti pravi čitač ekrana preko tabele. Identifikatori elemenata prosleđeni kroz BeginTagEx2 hrane stablo identifikatora (ISO 32000-1 §14.7.4), a upravo to čini vezivanje preko SetStructElemHeaders uopšte razrešivim
Ista porodica atributa pokriva i ostalo na šta se pomoćna tehnologija oslanja. SetStructElemListNumbering objavljuje kako su stavke liste označene, pa čitač ekrana najavljuje poziciju u listi umesto da recituje znakove za nabrajanje. SetStructElemBBox beleži granični okvir slika i tabela, koji prikazi sa preoblikovanjem koriste za smeštanje sadržaja. SetStructElemActualText daje zamenski tekst za nizove čiji se glifovi ne preslikavaju u čitljive znakove, kao što je inicijal sklopljen od vektorske grafike. Svaki od njih sledi isto pravilo: vezuje se za otvoren tag ili ispari
Artefakti, jezik i kapija dijagnostike pre čuvanja
Ponovljeni nameštaj stranice, dakle tekuća zaglavlja, oznake za presavijanje, vodeni žigovi i pozadinske nijanse, spada unutar zagrada BeginArtifact i EndArtifact da nikada ne uđe u tok čitanja. Jezik je nasledan. Podrazumevani jezik dokumenta dolazi iz argumenta poziva SetPDFUAMode, a niz na drugom jeziku ga prepisuje po elementu kroz BeginTagEx ili SetStructElemLang. Upravo to čuva izgovorljivost francuskog citata unutar engleskog priručnika
Pre čuvanja, GetPDFUADiagnostics pušta strukturne provere biblioteke preko dokumenta u memoriji i vraća nalaze kao tekst, pri čemu prazan string znači da ništa nije nađeno. Kodovi direktno imenuju klasične autorske greške: FIGURE-NO-ALT za sliku bez alternativnog teksta, HEADING-LEVEL-SKIP za H3 koji sledi posle H1, ROLEMAP-UNMAPPED za prilagođeni tip koji nikada nije registrovan. Uvežite ovo u izgradnju (generišite skup dokumenata, oborite korak na nepraznoj dijagnostici) i regresije pristupačnosti postaju otkazi u stilu greške pri prevođenju umesto revizorskih nalaza mesecima kasnije. Konačan sud o usaglašenosti i dalje pripada preflight proveri nad sačuvanim fajlom, opisanoj u tekstu o PDF/A i PDF/UA preflight proveri u Delphiju, jer se neke normalizacije primenjuju tek pri serijalizaciji
Kretanje kroz anotacije ima svoje dugme. PDF/UA očekuje da obilazak polja obrasca i veza tastaturom prati redosled strukture, a SetTabOrderMode upisuje unos za redosled tabulatora na nivou stranice koji pregledači poštuju, dok je GetTabOrderMode dostupan za reviziju dolaznih fajlova. To je vrsta zahteva koju niko ne primeti dok korisnik koji radi samo tastaturom ne prijavi grešku, a košta jedan poziv po dokumentu da bude urađen kako treba
Stabla strukture ne preživljavaju svako spajanje
Tagovani dokumenti ostaju tagovani samo kada svaki kasniji korak obrade sačuva stablo, a oštra ivica unutar PDF Library for Delphi je porodica poziva za spajanje liste. MergeFileListFast menja očuvanje stabla strukture za brzinu. To je ispravna razmena za serije skeniranih slika i pogrešna za tagovane izveštaje, jer se izlaz otvara bez greške, iscrtava se istovetno, a tiho je izgubio svoj sloj pristupačnosti. Koristite podrazumevani MergeFileList ili strogu varijantu kad god je bilo koji ulaz tagovan, i uvrstite IsTaggedPDF u tvrdnje posle sklapanja da spljoštena serija ne bi mogla da bude isporučena a da niko ne primeti. Lanci sklapanja za velike skupove dokumenata nose još ovakvih razmena, razmotrenih u tekstu o spajanju, deljenju i direktnom pristupu velikim PDF-ovima
Petlja provere zatvara se izvan biblioteke: otvorite izlaz u Acrobatu, pregledajte panel tagova i pročitajte bar jedan dokument po porodici šablona pravim čitačem ekrana. Dijagnostika hvata strukturne greške; jedino ljudsko uvo hvata redosled čitanja koji je tehnički ispravan, a praktično zbunjujući. Probne verzije i kompletna referenca API-ja za tagovanje nalaze se na stranici proizvoda losLab PDF Library for Delphi