Tehnički članak

Stabla strukture tagovanog PDF-a u Delphiju

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

Dijagram PDF Library for Delphi koji vezuje nizove označenog sadržaja sa celobrojnim MCID vrednostima za stablo strukture od H1, P i Figure preko mape uloga, dok su artefakti izuzeti iz redosleda čitanja
Celobrojne MCID vrednosti vezuju nizove označenog sadržaja za tipizirano stablo strukture, dok mapa uloga razrešava prilagođene uloge, a artefakti ostaju izvan redosleda čitanja

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

Dijagram tabele u PDF Library for Delphi koji pokazuje TH područje, colspan od dva i atribut headers vezan za ćelije podataka, uz pravilo da se pozivi za atribute vezuju samo dok je njihov tag otvoren
Čitači ekrana prate TH područje, colspan i vezivanja preko atributa headers umesto iscrtanih linija, a pozivi za atribute vezuju se samo dok je njihov tag otvoren

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

Dijagram PDF Library for Delphi u kom GetPDFUADiagnostics vraća prazan string ili imenovane nalaze poput FIGURE-NO-ALT koji obaraju izgradnju pre nego što preflight presudi o sačuvanom fajlu
GetPDFUADiagnostics prijavljuje nalaze poput FIGURE-NO-ALT pre čuvanja, a neprazan rezultat uvezan u izgradnju odmah obara korak

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