A PDFlibPas képes egy dokumentumot már a rajzolás során jelölni. Kapcsoljuk be a SetAutoTagMode-ot, és a mindennapi DrawText hívások bekezdések lesznek, a RegisterHeading után közvetlenül rajzolt szöveg az adott szintű címsorrá válik, a futó fejlécek és láblécek olyan artifacttá válnak, amelyet az olvasó átugrik, a képek ábrákká lesznek, a DrawTableRows pedig a táblázatot, sorait és celláit együtt viszi be a szerkezeti fába
A másik lehetőség — és a közelmúltig ez volt az egyetlen — minden rajzoló hívás kézi BeginTag és EndTag közé foglalása volt. Ez működik, és szokatlan szerkezetű dokumentumoknál ma is ez a megfelelő eszköz. Egy mindennapi jelentés, számla vagy kivonat esetén viszont azt jelenti, hogy a kimenet akadálymentessége attól függ, hogy senki nem felejt el egyetlen párt sem, minden olyan kódúton, amely bármit rajzol
Mit fednek le a mód-bitjei
A SetAutoTagMode egy bitmaszkot vesz át, és visszaadja a korábban érvényben lévő módot. Az AUTOTAG_TEXT (1) a szöveget bekezdésként jelöli, illetve címsorként, ha épp esedékes. Az AUTOTAG_FURNITURE (2) a futó fejléceket, lábléceket és oldalszámokat artifactként jelöli. Az AUTOTAG_FIGURE (4) egy rajzolt képet ábrává alakít, illetve artifacttá, ha azt deklaratívan dekoratívnak nyilvánították. Az AUTOTAG_TABLE (8) a rajzolt táblázatokat beviszi a szerkezeti fába. Az AUTOTAG_DEFAULT értéke 15, vagyis mind a négy
A mód bekapcsolása egyúttal tagged dokumentumként jelöli a fájlt, és ez a lépés kevésbé kozmetikai, mint amilyennek hangzik. Egy olvasó alapértelmezés szerint untaggednek tekinti a dokumentumot, hacsak a katalógus mást nem mond (ISO 32000-1 §14.7.1), tehát egy olyan fájl, amely teljes szerkezeti fát hordoz /MarkInfo deklaráció nélkül, a segítő technológia szerint egyáltalán nem rendelkezik szerkezettel. A fa ott van, csak semmi nem olvassa
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;
Honnan tud egy címsor, hogy melyik szöveghöz tartozik?
A RegisterHeading megnevezi a következő rajzolandó szöveg szintjét, és vár a szövegre. Ha közben egy képet rajzolunk, az ábrává válik, a címsor pedig függőben marad a rákövetkező szövegre. Ez a viselkedés szándékos: az a másik lehetőség, ahol a kép kapná meg a címsorszintet, olyan dokumentumokat eredményezett, ahol egy cím alatti dekoratív elválasztó vonal magaként a címként lett felolvasva
Ugyanez a „egy elemre elhasználódik” szabály érvényes az ábrákra is. A RegisterFigure megadja a leírást, amelyet a következő kép hordozni fog, a RegisterDecoration pedig olyan vonalként, szegélyként vagy háttérként jelöli meg a következő képet, amely semmilyen jelentést nem hordoz. Mindkettőt egyetlen kép fogyasztja el, így egy későbbi kép sosem örököl egy korábbihoz szánt leírást — így jönnek át a kézi jelölésű kódban az alt szövegek rossz képhez csatolva
A leírás többet számít, mint bármely más egyetlen karakterlánc egy akadálymentes dokumentumban. Egy látássérült olvasó a leírást kapja a kép helyett, és ez az egész, amit kap. A „Diagram” nem leírás; az „Negyedéves bevétel régiónként, a keleti régióval legmagasabban a Q3-ban” igen
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
Táblázatok, fejlécek és hol él az ismétlésre vonatkozó döntés
A táblázat bit bekapcsolásával a DrawTableRows a táblázatot, a sorait és a celláit beviszi a szerkezeti fába, így az olvasó meg tudja mondani, melyik oszlopban áll egy érték, ahelyett hogy a teljes táblázatot összefüggő, egymáshoz nem kapcsolódó szövegként olvasná fel. A SetTableHeaderRowCount megadja, hány kezdő sor minősül fejlécnek; ezek a sorok oszlophatókörrel rendelkező fejléccellaként íródnak ki, ami teszi lehetővé, hogy az olvasó felolvasja annak az értéknek a fejlécét, amelyen a felhasználó éppen áll
Az így megnevezett fejlécsorok ott maradnak, ahol vannak. Az oldalak tetején való ismétlésük elrendezési döntés, és annak is marad: a DrawTaggedTableRows egy RepeatHeaderRows argumentumot kap pontosan erre a célra. A kettő külön tartása elkeréli, hogy a szerkezeti fa minden oldaltörésnél egy másodpéldányt kapjon a fejlécből, amit az automatikus ismétlés pont előállítana
var
TableID: Integer;
begin
TableID := Lib.CreateTable(40, 3);
Lib.SetTableHeaderRowCount(TableID, 1); // row 1 is the header band
Lib.SetTableCellContent(TableID, 1, 1, 'Part');
Lib.SetTableCellContent(TableID, 1, 2, 'Torque');
Lib.SetTableCellContent(TableID, 1, 3, 'Unit');
// ... fill the data rows ...
// Draw rows 1..40 into a 600pt band, repeating one header row per page
Lib.DrawTaggedTableRows(TableID, 72, 150, 600, 1, 40, 1);
end;
Az automatikus és a kézi jelölés kombinálása
Az automatikus jelölés félreáll egy kézzel megnyitott címkén belül. Egy dokumentum egy részét a saját kódunk írhatja le, a többit rábízhatjuk a könyvtárra, anélkül hogy a kettő egymásba ágyazódna — ezt az elrendezést akarja a legtöbb valós dokumentum. A borlólapnak és az aláírási blokknak olyan szerkezete van, amit csak mi értünk; a kettő között lévő kétszáz oldal törzsszövegnek nincs
Két biztonsági szabály tartja tisztán a kimenetet. Egy artifacton belül semmi sem kerül jelölésre, mert az artifactként megjelölt tartalom nem hordozhat szerkezeti elemet. Az üres szöveg nem nyit elemet, tehát egy véletlen DrawText üres karakterlánccal nem hoz létre olyan szerkezeti elemet, amelyet az olvasó üresként olvasna fel. Mindkettő olyan hiba, amelyet a kézi jelölésű dokumentumok csendesen felhalmoznak, és egy validátor csak hónapokkal később, tömegesen jelent
Amit az automatikus jelölés ma sem dönt el helyetted
A rajzolási sorrenden túli olvasási sorrend, az olyan szemantikai szerepek, amelyek nem bekezdés, címsor, ábra vagy táblázat, illetve a nyelvdeklarációk. Az automatikus jelölés abban a sorrendben rendel szerkezetet, ahogy a tartalmat rajzoljuk — ha az elrendezési kód az oldalsávot a törzs előtt rajzolja, a fa azt a sorrendet rögzíti. Olyan dokumentumoknál, ahol a vizuális sorrend és az olvasási sorrend tényleg eltér, a kézi jelölési API marad a megfelelő eszköz, és a tagged PDF-ről és az akadálymentes szerkezetről szóló átjárás a szerepeket, a hatóköröket és a fejléckötéseket részletesen tárgyalja
Amikor a dokumentum kész, validáljunk ne feltételezzünk: a PDF/A és PDF/UA preflight jegyzetei megmutatják, hogyan kapjunk ítéletet a előállított szerkezetre, az adathalmaz-vezérelt jelentésexport átjárása pedig azt mutatja be, hol illeszkednek ezek a hívások egy olyan jelentésmotorba, amely az elrendezését adatokból generálja
A PDFlibPas egy natív Pascal PDF könyvtár Delphihez, C++Builderhez és Lazarushoz, külső PDF futtatókörnyezet nélkül, így az akadálymentes kimenetet ugyanaz a kód állítja elő, amely a dokumentumot rajzolja — a teljes API- és platformlistát a PDFlibPas termékoldal tartalmazza