Odborný článok

Automatické štruktúrne značenie pre prístupný PDF v Delphi

PDF Library for Delphi dokáže dokument značiť už počas jeho kreslenia. Zapnite SetAutoTagMode a bežné volania DrawText sa stanú odsekom, text nakreslený hneď po RegisterHeading sa stane nadpisom príslušnej úrovne, hlavičky a päty behom strany sa stanú artefaktmi, ktoré čítačka preskočí, obrázky sa stanú figúrami a DrawTableRows prenese tabuľku, jej riadky a bunky do stromu štruktúry

Alternatívou — a donedávna jedinou možnosťou — bolo ručné obaľovanie každého volania kreslenia do BeginTag a EndTag. To funguje a pre dokumenty s neobvyklou štruktúrou je to stále ten správny nástroj. Pre obyčajný report, faktúru či výpis to však znamená, že prístupnosť výstupu závisí od toho, že nikto nikdy nezabudne na pár, a to naprieč každou cestou v kóde, ktorá niečo kreslí

Čo pokrývajú bity režimu

SetAutoTagMode prijíma bitovú masku a vracia režim, ktorý bol doposiaľ aktívny. AUTOTAG_TEXT (1) značí text ako odsek alebo ako nadpis, ak je jeden na rade. AUTOTAG_FURNITURE (2) označuje hlavičky, päty a čísla strán ako artefakty. AUTOTAG_FIGURE (4) z nakresleného obrázku urobí figúru, prípadne artefakt, ak bol deklarovaný ako dekoratívny. AUTOTAG_TABLE (8) prenáša nakreslené tabuľky do stromu štruktúry. AUTOTAG_DEFAULT je 15, čo sú všetky štyri

Zapnutie režimu zároveň označí dokument ako značený a tento krok je menej kozmetický, než to znie. Čítačka považuje dokument za neznačený, ak katalóg nehovorí inak (ISO 32000-1 §14.7.1), takže súbor, ktorý nesie kompletný strom štruktúry bez deklarácie /MarkInfo, je asistívnymi technológiami ohlásený ako dokument bez akejkoľvek štruktúry. Strom tam je, no nič ho nečíta

Diagram bitových masiek SetAutoTagMode mapujúcich obyčajné vykresľovacie volania Delphi do označkovaného stromu štruktúry PDF, s príznakom Marked vyznačeným ako povinným
Každý aktívny bit metódy SetAutoTagMode smeruje bežné vykresľovacie volania do zodpovedajúceho prvku stromu štruktúry
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;

Odkiaľ nadpis vie, ku ktorému textu patrí?

RegisterHeading určuje úroveň pre ďalší nakreslený text a čaká, kým sa text objaví. Ak sa medzitým nakreslí obrázok, stane sa figúrou a nadpis zostáva čakať na text, ktorý príde po ňom. Toto správanie je zámerne: alternatíva, kde by obrázok prevzal úroveň nadpisu, produkovala dokumenty, v ktorých sa dekoratívna čiara pod titulom ohlasovala ako samotný titul

Rovnaké pravidlo „spotrebuje sa na jednu položku" platí pre figúry. RegisterFigure dodáva popis, ktorý bude niesť ďalší obrázok, a RegisterDecoration vyhlasuje ďalší obrázok za čiaru, rámik alebo pozadie, ktoré nenesie žiadny význam. Obe sa spotrebujú na jeden obrázok, takže neskorší obrázok nikdy nezdedí popis určený pre skorší — čo je presne spôsob, akým pri ručnom značení končí alternatívny text pripojený k nesprávnemu obrázku

Popis má väčší výnam než akýkoľvek iný reťazec v prístupnom dokumente. Nevidiaci čitateľ dostane popis namiesto obrázku a nič iné nedostane. „Graf" nie je popis; „Štvrťročné tržby podľa regiónov, pričom východný región je najvyšší v Q3" je

Diagram PDF Library for Delphi znázorňujúci registrovaný nadpis PDF čakajúci cez preťahujúci obrázok, kým RegisterFigure a RegisterDecoration sa každé spotrebuje na práve jeden obrázok
RegisterHeading čaká na ďalší text bez ohľadu na to, čo sa medzitým vykreslí
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

Tabuľky, hlavičky a kde žije rozhodnutie o opakovaní

S zapnutým bitom tabuľky DrawTableRows prenese tabuľku, jej riadky a bunky do stromu štruktúry, takže čítačka dokáže povedať, v ktorom stĺpci sa hodnota nachádza, namiesto toho, aby celú tabuľku čítala ako sériu nesúvisiaceho textu. SetTableHeaderRowCount určuje, koľko úvodných riadkov sú hlavičky; tieto riadky sa zapíšu ako hlavičkové bunky nesúce rozsah stĺpca, čo umožňuje čítačke oznámiť nadpis hodnoty, na ktorej sa používateľ nachádza

Riadky hlavičiek určené týmto spôsobom ostanú tam, kde sú. Ich opakovanie na vrchu každej strany je rozhodnutie o rozvrhnutí a tým aj ostáva: DrawTaggedTableRows preberá argument RepeatHeaderRows presne na tento účel. Udržanie oboch oddelených zabráni tomu, aby strom štruktúry získal druhú kópiu hlavičky pri každom zalomení strany, čo je presne to, čo by automatické opakovanie vyprodukovalo

Diagram PDF Library for Delphi s bunkami hlavičky tabuľky nesúcimi rozsah stĺpca v strome štruktúry, zatiaľ čo RepeatHeaderRows ponecháva opakovanie pri zlome strany len ako rozhodnutie o rozložení
Hlavičkové riadky žijú v strome štruktúry len raz, pričom rozsah stĺpca je pripojený ku každej bunke TH
var
  TableID: Integer;
begin
  TableID := Lib.CreateTable(40, 3);
  Lib.SetTableHeaderRowCount(TableID, 1);       // riadok 1 tvorí pás hlavičky
  Lib.SetTableCellContent(TableID, 1, 1, 'Part');
  Lib.SetTableCellContent(TableID, 1, 2, 'Torque');
  Lib.SetTableCellContent(TableID, 1, 3, 'Unit');
  // ... vyplňte riadky s údajmi ...
  // Vykreslite riadky 1..40 do pásu 600pt a na každej strane zopakujte jeden riadok hlavičky
  Lib.DrawTaggedTableRows(TableID, 72, 150, 600, 1, 40, 1);
end;

Zmiešanie automatického a ručného značenia

Automatické značenie sa stiahne vo vnútri značky otvorenej ručne. Časť dokumentu môže opísať váš kód a zvyšok nechať na knižnici, a to bez toho, aby sa do seba navzájom vnárali — čo je usporiadanie, ktoré si želá väčšina reálnych dokumentov. Obalová strana a blok podpisov majú štruktúru, ktorej rozumie iba váš kód; dvesto strán súvislého textu medzi nimi nie

Dve bezpečnostné pravidlá udržiavajú výstup čistý. Vo vnútri artefaktu sa nič neznačí, pretože obsah označený ako artefakt nesmie niesť žiadny element štruktúry. A prázdny text neotvorí žiadny element, takže odcudzené DrawText s prázdnym reťazcom nemôže vyprodukovať štruktúrny element, ktorý by čítačka ohlásila ako prázdny. Obe sú typom defektu, ktoré ručne značené dokumenty potichu hromadia a ktoré validátor nahlásihne hromadne o mesiace neskôr

Čo automatické značenie stále nerozhoduje za vás

Poradie čítania presahujúce poradie kreslenia, sémantické roly, ktoré nie sú odsek, nadpis, figúra ani tabuľka, a deklarácie jazyka. Automatické značenie priraďuje štruktúru v poradí, v akom je obsah kreslený — ak váš kód rozvrhnutia nakreslí bočný panel pred telom dokumentu, v takom poradí ho strom zaznamená. Pre dokumenty, kde sa vizuálne poradie a poradie čítania skutočne líšia, zostáva ručné API značenia tým správnym nástrojom a návod k značenému PDF a štruktúre prístupnosti podrobne pokrýva role, rozsahy a väzby hlavičiek

Keď je dokument dokončený, validujte namiesto predpokladu: poznámky k PDF/A a PDF/UA preflightu ukazujú, ako získať verdikt nad vyprodukovanou štruktúrou, a návod k exportu reportu riadenému dátami pokrýva, kam tieto volania patria v reportovacom engine, ktorý generuje rozvrhnutie z dát

PDF Library for Delphi je natívna Pascal PDF knižnica pre Delphi, C++Builder a Lazarus bez externého PDF runtime, takže prístupný výstup produkuje ten istý kód, ktorý kreslí dokument — kompletný zoznam API a platforiem nájdete na produktovej stránke PDF Library for Delphi