Prístupné PDF sa opiera o jednu štruktúru, ktorú viditeľná stránka nikdy nezobrazuje: štruktúrny strom definovaný v ISO 32000-1 §14.7. Ide o logickú hierarchiu nadpisov, odsekov, tabuliek a obrázkov, ktorá je navrstvená nad vykresleným obsahom a priradená k štandardným rolám prostredníctvom mapy rolí (role map). Čítačka obrazovky číta tento strom, nie samotné znaky na stránke. Bez neho je vygenerovaná faktúra, ktorá vyzerá bezchybne, sémanticky prázdna, pretože prúd obsahu zaznamenáva iba poradie vykresľovania a nič iné. Celková suma môže byť prečítaná skôr ako jednotlivé položky, päta môže rozdeliť odsek a tabuľka položiek sa môže zliať do jedného neprerušeného toku slov. Náklady na zabránenie tomuto stavu sú však minimálne v porovnaní s výhodami. Generovanie štruktúry počas kreslenia je záležitosťou niekoľkých minút kódu; jej dodatočné dopĺňanie do hotových dokumentov je náročný projekt. Knižnica losLab PDF Library (PDFlibPas) sprístupňuje tento strom pre Delphi a C++Builder prostredníctvom malej sady volaní, ktoré obalia každú kresliacu operáciu do jej logickej roly
Ako sa označený obsah viaže na štruktúrny strom
Spolupracujú dve vrstvy. V prúde obsahu sú kresliace operácie uzatvorené do sekvencií označeného obsahu (marked content), pričom každá z nich nesie celočíselný identifikátor MCID. V katalógu dokumentu štruktúrny strom mapuje tieto MCID do hierarchie typovaných prvkov (H1, P, Table, Figure) s atribútmi, ako sú alternatívny text a jazyk. Vlastné typy prvkov sú povolené, ale každý z nich sa musí rozlíšiť na štandardnú rolu prostredníctvom mapy rolí (ISO 32000-1 §14.8.4). Obsah, ktorý nemá žiadny význam, ako sú čiary, pozadia a opakujúce sa prvky stránky, sa označí ako artefakt, aby ho asistenčné technológie preskočili a nečítali uprostred vety
PDFlibPas spravuje obe vrstvy pod jedným párom značiek. BeginTag otvorí štruktúrny prvok a spustí sekvenciu označeného obsahu, do ktorej smerujú volania kreslenia, a EndTag zatvorí obe. Správa údajov, pri ktorej dochádza k chybám pri ručnom tagovaní (teda MCID, rodičovský strom a odkazy na stránky), prebieha interne, takže ju nemôžete nastaviť nesprávne
Pred otvorením akejkoľvek značky prácu definujú dva prepínače na úrovni dokumentu. Metóda SetMarkInfo zapíše do katalógu príznak, ktorý deklaruje, že dokument je tagovaný, a IsTaggedPDF ho načíta späť, čo je rýchly prvotný test pri rozhodovaní, či má prichádzajúci súbor štruktúru, ktorú sa oplatí zachovať. Nastavenie jazyka má dva vstupné body. Metóda SetDocumentLanguage samostatne nastavuje predvolený jazyk dokumentu, zatiaľ čo SetPDFUAMode ho nastavuje ako súčasť aktivácie plného výstupu PDF/UA. Súbor môže byť užitočne tagovaný aj bez deklarovania zhody s PDF/UA, a postupná implementácia často začína práve tu
Tagovanie počas kreslenia, nie dodatočne
Osvedčeným postupom je považovať značky tagovania za súčasť podpisu každého volania kreslenia, nikdy nie ako dodatočný krok:
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 volania v tejto sekvencii majú zásadný význam pre zhodu s normami. Metóda SetPDFUAMode povoľuje výstup PDF/UA a potichu zvýši verziu dokumentu na PDF 1.7, čo koliduje s fixovaním verzie. Dokument uzamknutý na verziu PDF 1.4 pomocou LockSaveVersion sa odmietne uložiť a po aktivácii režimu UA vráti kód chyby 602. Tento konflikt sa zvyčajne objaví, keď archivačné profily a požiadavky na prístupnosť konfigurujú rôzne tímy. Volanie SetInformation(1, ...) zapíše názov dokumentu, ktorý norma ISO 14289 vyžaduje od prehliadačov zobrazovať namiesto názvu súboru; jeho absencia je jedným z najčastejších nedostatkov pri auditoch PDF/UA. Metóda AddRoleMap zaregistruje vlastný typ ManualTitle ako H1, pričom jej vynechanie spôsobí, že diagnostika popísaná nižšie označí túto rolu ako nemapovanú
Úrovne nadpisov si vyžadujú premyslenú koncepciu, nie ad-hoc rozhodnutia založené na vzhľade stránky. Používatelia čítačiek obrazovky prechádzajú medzi sekciami pomocou klávesových skratiek pre nadpisy, takže šablóna, ktorá prechádza z H1 na H3 len preto, že stredná úroveň vyzerala vo vizuálnom dizajne príliš veľká, potichu naruší túto navigáciu a žiadna vizuálna kontrola to neodhalí. Presne toto je chyba, pre ktorú existuje diagnostické označenie HEADING-LEVEL-SKIP. Namapujte vizuálne štýly každej šablóny na fixnú štruktúru nadpisov raz, na jednom mieste, a k takémuto posunu už nedôjde
Tabuľky, v ktorých sa čítačka obrazovky dokáže orientovať
Nakreslené čiary mriežky mimo obrazovky nič neznamenajú. To, v čom sa čítačky obrazovky orientujú, sú štrukturálne vzťahy: ktoré bunky sú hlavičky, ktoré bunky riadia a ako sa dátové bunky viažu na hlavičky v nepravidelných rozloženiach. Volania atribútov štruktúrnych prvkov spracovávajú všetky tri tieto aspekty:
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
Pravidlo poradia je prísne a ticho vynucované. Každé volanie SetStructElem* sa vzťahuje na značku (tag), ktorá je v danom momente otvorená, teda medzi jej BeginTag a EndTag. Ak nie je otvorená žiadna značka alebo sa atribút nevzťahuje na aktuálnu značku, vráti hodnotu 0 bez vyvolania chyby. Nesprávne umiestnené volanie sa jednoducho nevykoná. Zabalením návratových hodnôt do asercií (assertions) počas vývoja zachytíte tieto odchýlky hneď; inak sa chýbajúci rozsah (scope) prejaví až vtedy, keď audit prístupnosti otestuje tabuľku skutočnou čítačkou obrazovky. Identifikátory prvkov odovzdané cez BeginTagEx2 napĺňajú strom ID (ISO 32000-1 §14.7.4), čo je predpokladom na to, aby väzba SetStructElemHeaders vôbec fungovala
Rovnaká rodina atribútov pokrýva aj ostatné prvky, o ktoré sa opierajú asistenčné technológie. Metóda SetStructElemListNumbering deklaruje, ako sú označené položky zoznamu, takže čítačka obrazovky oznámi pozíciu v zozname namiesto čítania znakov odrážok. Volanie SetStructElemBBox zaznamenáva ohraničenie (bounding box) obrázkov a tabuliek, ktoré zobrazenia s preusporiadaním obsahu (reflow) používajú na jeho umiestnenie. Metóda SetStructElemActualText poskytuje náhradný text pre úseky, ktorých znaky sa nemapujú na čitateľné znaky, ako je napríklad iniciála (drop cap) vytvorená z vektorovej grafiky. Každá z nich sa riadi rovnakým pravidlom: viaže sa na otvorenú značku, alebo sa nevykoná
Artefakty, jazyk a diagnostická kontrola pred uložením
Opakujúce sa prvky stránky, ako sú záhlavia, značky na prehnutie papiera, vodotlače a tónovanie pozadia, patria do blokov BeginArtifact a EndArtifact, aby sa nedostali do čítaného toku textu. Jazyk je dedičný. Predvolený jazyk dokumentu pochádza z argumentu SetPDFUAMode a úsek v inom jazyku ho prepíše pre konkrétny prvok pomocou BeginTagEx alebo SetStructElemLang. To zabezpečuje, že napríklad francúzsky citát v anglickom manuáli bude správne vyslovený
Pred uložením spustí metóda GetPDFUADiagnostics štrukturálne kontroly knižnice nad dokumentom v pamäti a vráti zistenia ako text, pričom prázdny reťazec znamená, že sa nenašli žiadne chyby. Kódy priamo pomenúvajú klasické chyby pri tvorbe: FIGURE-NO-ALT pre obrázok bez alternatívneho textu, HEADING-LEVEL-SKIP pre H3 nasledujúci po H1 a ROLEMAP-UNMAPPED pre vlastný typ, ktorý nebol nikdy zaregistrovaný. Ak toto prepojíte so zostavením projektu (vygenerovanie sady dokumentov a zlyhanie kroku v prípade neprázdnej diagnostiky), regresie prístupnosti sa stanú chybami už vo fáze kompilácie namiesto zistení auditu o niekoľko mesiacov neskôr. Úplné posúdenie zhody však stále patrí predletovej kontrole (preflight) uloženého súboru, ktorá je opísaná v článku predletová kontrola PDF/A a PDF/UA v Delphi, pretože niektoré normalizácie sa aplikujú až počas serializácie
Navigácia v anotáciách má svoje vlastné nastavenie. PDF/UA vyžaduje, aby prechádzanie formulárových polí a odkazov pomocou klávesnice sledovalo poradie štruktúry, a metóda SetTabOrderMode zapisuje položku tabulátora na úrovni stránky, ktorú prehliadače rešpektujú, pričom GetTabOrderMode je k dispozícii na kontrolu prichádzajúcich súborov. Ide o typ požiadavky, ktorú si nikto nevšimne, kým používateľ odkázaný na klávesnicu nenahlási chybu, pričom na jej správne nastavenie stačí jedno volanie na dokument
Štruktúrne stromy neprežijú každé zlúčenie
Tagované dokumenty zostávajú tagované len vtedy, ak každý ďalší krok spracovania zachová strom štruktúry. Rizikovým miestom v PDFlibPas je rodina funkcií pre zlučovanie. Metóda MergeFileListFast mení zachovanie štruktúrneho stromu za rýchlosť. To je správna voľba pre sady naskenovaných obrázkov, ale nesprávna pre tagované reporty, pretože výstup sa otvorí v poriadku, vykreslí sa rovnako, ale potichu stratí svoju vrstvu prístupnosti. Ak je na vstupe tagovaný dokument, použite predvolenú metódu MergeFileList alebo jej striktný variant a urobte z IsTaggedPDF súčasť asercií po zostavení, aby sa plochý súbor nedostal do distribúcie bez povšimnutia. Zostavovacie reťazce pre veľké sady dokumentov prinášajú viac podobných kompromisov, ktoré sú opísané v článku spájanie, rozdeľovanie a priamy prístup k veľkým PDF
Overovací cyklus sa uzatvára mimo knižnice: otvorte výstup v programe Acrobat, skontrolujte panel tagov a prečítajte aspoň jeden dokument z každej rodiny šablón pomocou skutočnej čítačky obrazovky. Diagnostika zachytí štrukturálne chyby; avšak iba ľudské ucho dokáže odhaliť poradie čítania, ktoré je technicky platné, ale prakticky mätúce. Skúšobné verzie a kompletná referenčná príručka pre tagovacie API sa nachádzajú na produktovej stránke losLab PDF Library pre Delphi