Műszaki cikk

Címkézett PDF struktúrafák Delphiben

Az akadálymentes PDF olyan szerkezeten nyugszik, amelyet a látható oldal soha nem mutat meg: az ISO 32000-1 §14.7 szerinti struktúrafán. Ez címsorok, bekezdések, táblázatok és ábrák logikai hierarchiája, amely a kifestett tartalom fölé rétegződik, és szereptérképen át képződik le a szabványos szerepekre. A képernyőolvasó ezt a fát olvassa fel, nem az oldalra tett jeleket. Nélküle egy makulátlanul kinéző, generált számla jelentés szempontjából üres, mert a tartalomfolyam csak a rajzolási sorrendet rögzíti, semmi mást. A végösszeg elhangozhat a tételsorok előtt, a lábléc belevághat egy bekezdésbe, a tételtáblázat pedig szavak egyetlen tagolatlan folyamává eshet össze. Ennek megelőzési költsége az Ön javára billen. A szerkezet kibocsátása rajzolás közben percnyi kód; utólag beleépíteni kész dokumentumokba már helyreállítási projekt. A losLab PDF Library (PDF Library for Delphi) néhány hívásból álló kis készlettel teszi elérhetővé a fát Delphi és C++Builder alatt, és ezek minden rajzolási műveletet a saját logikai szerepébe csomagolnak

Hogyan kötődik a megjelölt tartalom a struktúrafához?

Két réteg működik együtt. A tartalomfolyamban a rajzolási műveletek megjelölt tartalmi szakaszokba kerülnek zárójelbe, amelyek mindegyike egész számú MCID értéket hordoz. A dokumentumkatalógusban a struktúrafa ezeket az MCID értékeket típusos elemek (H1, P, Table, Figure) hierarchiájába képezi le olyan attribútumokkal, mint az alternatív szöveg és a nyelv. Egyedi elemtípusok is szabályosak, de mindegyiknek szabványos szerepre kell feloldódnia a szereptérképen át (ISO 32000-1 §14.8.4). Az a tartalom, amely egyáltalán nem hordoz jelentést — vonalak, hátterek, ismétlődő oldalkellékek —, artifactként jelölendő, hogy a segítő technológia átugorja ahelyett, hogy mondat közben felolvasná

A PDF Library for Delphi mindkét réteget egyetlen zárójelpár mögött tartja karban. A BeginTag struktúraelemet nyit és elindítja a megjelölt tartalmi szakaszt, a rajzolási hívások ebbe esnek, az EndTag pedig mindkettőt lezárja. Az a könyvelés, amely a kézzel gyártott címkézést elgáncsolja — az MCID értékek, a szülőfa és az oldalhivatkozások —, belül történik, ahol nem tudja elrontani

A PDF Library for Delphi ábrája, amely egész számú MCID értékeket hordozó megjelölt tartalmi szakaszokat köt H1, P és Figure struktúrafához a szereptérképen át, miközben az artifactok kimaradnak az olvasási sorrendből
Az egész számú MCID értékek típusos struktúrafához kötik a megjelölt tartalmi szakaszokat, miközben a szereptérkép feloldja az egyedi szerepeket, az artifactok pedig az olvasási sorrenden kívül maradnak

Két dokumentumszintű kapcsoló keretezi a munkát, mielőtt bármely címke megnyílna. A SetMarkInfo írja ki azt a katalógusjelzőt, amely címkézettnek nyilvánítja a dokumentumot, az IsTaggedPDF pedig visszaolvassa, és ez az olcsó első vizsgálat, amikor arról dönt, van-e egy beérkező fájlban megőrzésre érdemes szerkezet. A nyelvnek két belépési pontja van. A SetDocumentLanguage önmagában állítja be a dokumentum alapértelmezését, míg a SetPDFUAMode a teljes PDF/UA kimenet bekapcsolásának részeként teszi ezt. Egy fájl hasznosan címkézhető anélkül is, hogy PDF/UA megfelelőséget állítana magáról, és a szakaszos bevezetés gyakran pontosan ott kezdődik

Címkézés rajzolás közben, nem utólag

A működő generálási minta az, hogy a címkezárójelet minden rajzolási hívás aláírásának részeként kezeli, soha nem későbbi menetként:

var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);                          // bal felső origó
    Lib.SetPDFUAMode('en-US');                 // a mentési verziót PDF 1.7-re emeli
    Lib.SetInformation(1, 'Service Manual');   // a /Title kötelező a PDF/UA-hoz
    Lib.AddRoleMap('ManualTitle', 'H1');       // egyedi típus -> szabványos szerep
    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');               // oldaldíszítés: kimarad az olvasásból
    // ... vonalak és háttérszínezés rajzolása ...
    Lib.EndArtifact;
    Lib.SaveToFile('manual.pdf');
  finally
    Lib.Free;
  end;
end;

Ebben a sorozatban három hívás hordoz megfelelőségi súlyt. A SetPDFUAMode bekapcsolja a PDF/UA kimenetet, és némán PDF 1.7-re emeli a dokumentumverziót, ami ütközik a verziórögzítéssel. Az a dokumentum, amelyet a LockSaveVersion PDF 1.4-re zárt, UA mód mellett megtagadja a mentést, és 602-es hibakódot ad vissza — ez az összeütközés jellemzően akkor bukkan fel, amikor az archiválási profilokat és az akadálymentességi követelményeket különböző csapatok állítják be. A SetInformation(1, ...) a dokumentum címét írja ki, amelyet az ISO 14289 szerint a megjelenítőknek a fájlnév helyett kell mutatniuk; ennek hiánya a vadonban az egyik leggyakoribb PDF/UA megállapítás. Az AddRoleMap az egyedi ManualTitle típust H1 szerepként regisztrálja, kihagyása esetén pedig az alább leírt diagnosztika feltérképezetlen szerepet jelez

A címsorszintek átgondolt irányelvet érdemelnek, nem az oldal kinézete alapján hozott alkalmi döntéseket. A képernyőolvasót használók címsor-billentyűparanccsal ugrálnak a szakaszok között, tehát az a sablon, amely H1 szintről H3 szintre lép, mert a köztes szint túl nagynak tűnt a látványtervben, csendben tönkreteszi ezt a navigációt, és semmilyen vizuális átnézés nem fogja elkapni. Pontosan ez az a hiba, amelynek megnevezésére a HEADING-LEVEL-SKIP diagnosztika létezik. Képezze le minden sablon vizuális stílusait egyszer, egyetlen helyen, rögzített címsorlétrára, és az elcsúszás el sem kezdődik

Táblázatok, amelyekben a képernyőolvasó valóban navigálni tud

A megrajzolt rácsvonalak képernyőn kívül semmit sem jelentenek. A képernyőolvasók szerkezeti kapcsolatokban navigálnak: mely cellák fejlécek, mit ural az egyes fejlécek, és hogyan kötődnek az adatcellák a fejlécekhez szabálytalan elrendezésekben. A struktúraelem-attribútum hívásai mind a hármat kezelik:

Lib.BeginTag('Table', '', '');
Lib.BeginTag('TR', '', '');
Lib.BeginTagEx2('TH', '', '', '', '', 'col-part', '');
Lib.SetStructElemScope('Column');          // csak addig érvényes, amíg ez a TH nyitva van
Lib.DrawText(72, 120, 'Part');
Lib.EndTag;
Lib.BeginTagEx2('TH', '', '', '', '', 'col-torque', '');
Lib.SetStructElemScope('Column');
Lib.SetStructElemColSpan(2);               // a fejléc az érték- és a mértékegység-oszlopra terjed ki
Lib.DrawText(200, 120, 'Tightening torque');
Lib.EndTag;
Lib.EndTag;
Lib.BeginTag('TR', '', '');
Lib.BeginTag('TD', '', '');
Lib.SetStructElemHeaders('col-part');      // kifejezett kötés szabálytalan táblázatokhoz
Lib.DrawText(72, 140, 'M8 flange bolt');
Lib.EndTag;
Lib.EndTag;
Lib.EndTag; // Table

A sorrendi szabály szigorú, és némán érvényesül. Minden SetStructElem* hívás arra a címkére vonatkozik, amely abban a pillanatban nyitva van, a BeginTag és az EndTag hívása között, és 0 értéket ad vissza minden jelzés nélkül, ha nincs nyitott címke, vagy az attribútum nem vonatkozik az aktuálisra. Egy rossz helyre tett hívás egyszerűen elpárolog. Ha fejlesztés közben állításokba csomagolja a visszatérési értékeket, akkor még idejében elkapja az elcsúszást; magára hagyva egy hiányzó hatókör csak akkor derül ki, amikor egy akadálymentességi ellenőrzés valódi képernyőolvasót futtat végig a táblázaton. A BeginTagEx2 hívásán átadott elemazonosítók az azonosítófát táplálják (ISO 32000-1 §14.7.4), és eleve ettől lesz feloldható a SetStructElemHeaders kötése

Ugyanez az attribútumcsalád fedi le mindazt, amire a segítő technológia támaszkodik. A SetStructElemListNumbering deklarálja, hogyan címkézettek a listaelemek, így a képernyőolvasó a listán belüli helyzetet mondja be a felsorolásjelek felsorolása helyett. A SetStructElemBBox rögzíti az ábrák és táblázatok befoglaló keretét, amelyet az újratördelő nézetek használnak a tartalom elhelyezésére. A SetStructElemActualText helyettesítő szöveget ad azokhoz a szakaszokhoz, amelyek jelalakjai nem képezhetők le olvasható karakterekre, például egy vektorgrafikából összerakott iniciáléhoz. Mindegyik ugyanazt a szabályt követi: a nyitott címkéhez kötődik, vagy elpárolog

A PDF Library for Delphi táblázatábrája, amely a TH hatókörét, a kettes oszlopátfogást és az adatcellákhoz kötő headers attribútumot mutatja, mellette azzal a szabállyal, hogy az attribútumhívások csak nyitott címkéhez kötődnek
A képernyőolvasók a TH hatókörét, az oszlopátfogást és a headers kötéseket követik a megrajzolt vonalak helyett, az attribútumhívások pedig csak nyitott címkéjük mellett kötődnek

Artifactok, nyelv és a mentés előtti diagnosztikai kapu

Az ismétlődő oldalkellékek — vagyis az élőfejek, a hajtásjelek, a vízjelek és a háttérszínezések — a BeginArtifact és az EndArtifact zárójelei közé valók, hogy sose kerüljenek be az olvasási folyamba. A nyelv örökölhető. A dokumentum alapértelmezése a SetPDFUAMode argumentumából jön, egy másik nyelvű szakasz pedig elemenként írja felül a BeginTagEx vagy a SetStructElemLang hívással. Ettől marad kiejthető egy francia idézet egy angol kézikönyvben

Mentés előtt a GetPDFUADiagnostics lefuttatja a könyvtár szerkezeti ellenőrzéseit a memóriabeli dokumentumon, és szövegként adja vissza a megállapításokat, ahol az üres karakterlánc azt jelenti, hogy semmit sem talált. A kódok közvetlenül nevezik meg a klasszikus szerkesztési hibákat: a FIGURE-NO-ALT az alternatív szöveg nélküli képet, a HEADING-LEVEL-SKIP a H1 után következő H3 szintet, a ROLEMAP-UNMAPPED pedig a soha nem regisztrált egyedi típust. Drótozza ezt a fordítási folyamatba (állítsa elő a dokumentumkészletet, és buktassa el a lépést nem üres diagnosztika esetén), és az akadálymentességi visszalépésekből fordításidő-szerű hibák lesznek ahelyett, hogy hónapokkal későbbi ellenőrzési megállapítások lennének. A teljes megfelelőségi ítélet továbbra is a mentett fájl előellenőrzéséé, amelyet a PDF/A és PDF/UA előellenőrzés Delphiben tárgyal, mert bizonyos normalizálások csak a sorosítás során történnek meg

A PDF Library for Delphi ábrája arról, hogy a GetPDFUADiagnostics üres karakterláncot vagy megnevezett megállapításokat, például FIGURE-NO-ALT kódot ad vissza, amelyek elbuktatják a fordítást, mielőtt az előellenőrzés megítélné a mentett fájlt
A GetPDFUADiagnostics még mentés előtt jelenti az olyan megállapításokat, mint a FIGURE-NO-ALT, és a fordításba drótozott nem üres eredmény azonnal elbuktatja a lépést

A jegyzetek közti navigációnak saját gombja van. A PDF/UA elvárja, hogy az űrlapmezők és hivatkozások billentyűzetes bejárása a szerkezeti sorrendet kövesse, és a SetTabOrderMode írja ki azt az oldalszintű tabulátorsorrend-bejegyzést, amelyet a megjelenítők tiszteletben tartanak, míg a GetTabOrderMode a beérkező fájlok ellenőrzéséhez áll rendelkezésre. Ez olyan követelmény, amelyet senki sem vesz észre, amíg egy csak billentyűzetet használó felhasználó hibát nem jelent, a helyes megoldás pedig dokumentumonként egyetlen hívásba kerül

A struktúrafák nem élnek túl minden egyesítést

A címkézett dokumentumok csak akkor maradnak címkézettek, ha minden későbbi feldolgozási lépés megőrzi a fát, és a PDF Library for Delphi éles pereme az egyesítési lista családja. A MergeFileListFast a struktúrafa megőrzését váltja sebességre. Ez a helyes alku beolvasott képkötegeknél és a helytelen címkézett jelentéseknél, mert a kimenet rendben megnyílik, azonosan rajzolódik ki, és csendben elvesztette az akadálymentességi rétegét. Használja az alapértelmezett MergeFileList hívást vagy a szigorú változatot, valahányszor bármely bemenet címkézett, és tegye az IsTaggedPDF hívást az összeállítás utáni ellenőrzések részévé, hogy egy laposra vasalt köteg ne juthasson ki anélkül, hogy bárki észrevenné. A nagy dokumentumkészletek összeállítási munkafolyamatai több ilyen alkut hordoznak, amelyeket a nagy PDF-ek egyesítéséről, darabolásáról és közvetlen eléréséről szóló cikk jár körül

Az ellenőrzési kör a könyvtáron kívül zárul: nyissa meg a kimenetet Acrobatban, vizsgálja meg a címkepanelt, és sabloncsaládonként legalább egy dokumentumot olvastasson fel valódi képernyőolvasóval. A diagnosztika a szerkezeti hibákat kapja el; azt az olvasási sorrendet, amely technikailag érvényes, gyakorlatilag mégis zavarba ejtő, csak emberi fül fogja meg. A kiértékelési változatok és a teljes címkézési API-hivatkozás a losLab PDF Library for Delphi termékoldalán találhatók