Van egy külső invoice sablonod, vagy egy archivált szerződésed, amelyet évekkel ezelőtt olyan szoftverrel generáltak, amit már senki nem talál meg, és a követelmény az, hogy interaktívvá tedd: tegyél egy aláírásmezőt a sarokba, adj hozzá néhány szövegmezőt, és a lapos ellenőrzőlistából csinálj valódi jelölőnégyzeteket. A csavar az, hogy ezt a PDF-et nem a nulláról írod. Már létezik, már vannak benne oldalak, tartalmi streamek és általad nem kontrollált betűkészletek, és erre az objektumgráfra kell AcroForm widgeteket ráépítened anélkül, hogy újraépítenéd. Ez más feladat, mint egy új dokumentumon űrlapot létrehozni, és az a rész, ahol az emberek elakadnak, egészen addig láthatatlan, amíg meg nem nyitják az eredményt egy megjelenítőben, és a frissen írt mezők sehol sincsenek az oldalon
A HotPDF egy natív VCL PDF komponens Delphire és C++Builderre, és a v2.247.0-tól kezdve egy külön metóduscsoportot ad pontosan ehhez: mind a hat standard mezőtípus felépítéséhez közvetlenül egy LoadFromFile-lel betöltött dokumentumon. Ez a cikk végigvezet azon, mit csinálnak ezek a metódusok, milyen ISO 32000-1 szótárat építenek, és melyik az az egyetlen kapcsoló, amely nélkül az egész művelet csendben üresnek tűnő fájlt eredményez
Miért saját kódelág a betöltött dokumentumon történő mezőlétrehozás
Amikor a PDF-et a semmiből építed, a HotPDF birtokolja a teljes objektummodellt. Minden oldal egy írható THPDFPage burkoló, és egy szövegmező AddTextField-del való hozzáadása beköti az új widgetet az oldal annotációs objektumába, az oldalobjektumba és az űrlap mezőgyűjteményébe, majd egy megjelenési streamet generál a dokumentum betűforrásaiból. A megjelenési stream a widget látható felülete, a keret, a mező és az esetleges alapértelmezett szöveg, amelyet a megjelenítő PDF rajzi parancsként jelenít meg
Az egy betöltött dokumentum viszont semmilyen ilyen állványzatot nem ad. Az oldalak nyers szótárként érkeznek, nincs írható THPDFPage burkoló, amelyre widgetet lehetne akasztani, és ami még fontosabb, nincs készen álló betűforrás-pipeline, amely megrajzolná a megjelenési streameket. Ezért a betöltött út másik megoldást választ. A mezőszótárakat közvetlenül a beolvasott objektumgráfra írja rá, és az oldalakat nullától indexelt számmal azonosítja, nem oldalobjektummal. A mezőtípusok és a flag bitek pontosan ugyanazok, mint a nulláról épített úton, tehát a Text mező mindkét esetben Text mező; ami változik, az az alatta futó plumbing és, ami döntőbb, az, hogy hogyan rajzolódik meg a widget felülete
A /NeedAppearances kapcsoló itt nem opcionális
Ez az az egyetlen tény, amely eldönti, látszik-e a munkád. Mivel a betöltött út nem generál megjelenési streameket, a frissen hozzáadott widget megjelenési utasítás nélkül érkezik az olvasóhoz: nincs /AP bejegyzése, vagyis nincs leírt felülete. Sok megjelenítő, ha olyan widgetet kap, amelynek nincs megjelenése és nincs utasítás a létrehozására, egyszerűen semmit sem rajzol. A mező benne van a fájlban, szerkezetileg érvényes, űrlaptöltő eszközzel elérhető, és az ember számára teljesen láthatatlan
A menekülő útvonalat az ISO 32000-1 §12.7.3 határozza meg: az AcroForm szótár tartalmaz egy /NeedAppearances logikai értéket, és amikor az true, a szabványos olvasónak magának kell felépítenie a hiányzó megjelenési streameket az egyes mezők /DA alapértelmezett megjelenési sztringje és értéke alapján. A HotPDF ezt helyetted beállítja. Az első alkalommal, amikor bármilyen mezőt hozzáadsz egy betöltött dokumentumhoz, lefut az EnsureLoadedAcroForm: ha a katalógusban nincs /AcroForm, létrehozza, ha nincs /Fields tömb, azt is létrehozza, és kikényszeríti a /NeedAppearances true értéket. Nem hívod meg közvetlenül, de ha tudod, hogy létezik, a viselkedés érthetővé válik. Ez egy fontos telepítési kitételt is megmagyaráz: néhány minimális vagy nem szabványos megjelenítő figyelmen kívül hagyja a /NeedAppearances értéket, és továbbra is semmit sem rajzol. A mainstream olvasóknál a flag elvégzi a dolgát, de ha a közönséged valamilyen szokatlan beágyazott renderert használ, ott tesztelj, mielőtt bármit is ígérsz
A hat mezőtípus hozzáadása
Minden metódus ugyanazt a sémát követi. Átadod a nullától indexelt oldalszámot, a widget téglalapjának négy sarkát PDF user-space koordinátákban, a mező nevét, és minden további argumentumot, amelyet az adott típus megkíván. A téglalap X1, Y1, X2, Y2 formájú, a PDF origója pedig az oldal bal alsó sarka, ezért a nagyobb Y értékek feljebb vannak. Ez a fájlformátum koordináta-konvenciója, nem a képernyő bal felső sarokból induló rendszere, és ezt eltéveszteni a második leggyakoribb hiba a kapcsoló elfelejtése után. Minden hívás a létrehozott mező nullától indexelt sorszámát adja vissza, vagy -1-et, ha az oldalszám kívül esett a tartományon, vagy az oldalobjektumot nem lehetett feloldani
var
Pdf: THotPDF;
Idx: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('contract.pdf') <= 0 then Exit;
// Text field: name, initial value, max length (0 = unlimited)
Idx := Pdf.AddLoadedTextField(0, 72, 680, 320, 700, 'FullName', '', 0);
// CheckBox: export value, initial checked state
Pdf.AddLoadedCheckBox(0, 72, 640, 90, 658, 'AgreeTerms', 'Yes', False);
// Signature field: just a name and a rectangle
Pdf.AddLoadedSignatureField(0, 360, 72, 540, 132, 'ApproverSig');
if Idx >= 0 then
Pdf.SaveLoadedDocument('contract-interactive.pdf');
finally
Pdf.Free;
end;
end;
A szövegmező harmadik és negyedik sztring argumentuma a mező neve és a kezdeti /V értéke; az egész szám a /MaxLen, amely csak akkor íródik ki, ha nagyobb nullánál. A HotPDF minden szerkeszthető mezőnek alapértelmezett megjelenési sztringként a /Helv 12 Tf 0 0 0 rg értéket adja, és ezt a /NeedAppearances-t tiszteletben tartó megjelenítő használja arra, hogy eldöntse, milyen betűvel és színnel rajzolja ki az értéket. A jelölőnégyzet egy export értéket vár, vagyis azt a sztringet, amelyet az űrlap elküld, amikor a négyzet be van pipálva, valamint egy logikai értéket a kezdeti állapothoz; belül ehhez a megfelelő /V, /AS és /DV névbejegyzéseket írja, hogy a ki- és bekapcsolt állapot már a fájl megnyitásakor következetes legyen. Ha az export érték üres, akkor a szokásos checkbox "on" névre, vagyis Yes-re áll
Választómezők és a /Ff bitflag-ek
A ComboBox és a ListBox mindkettő választómező, ISO 32000-1 §12.7.4 szerint /Ch mezőtípus. A lenyíló lista és a görgethető lista közötti különbség egyetlen bit a /Ff mezőflagszámában: a 18. bit, a Combo flag, értéke $40000. A HotPDF ezt a bitet az AddLoadedComboBox esetén beállítja, az AddLoadedListBox esetén pedig nem; egyébként a kettő azonos, és mindkettő a választási lehetőségeket string tömbként írja a /Opt bejegyzésbe
// Dropdown (Combo flag set internally) with an initial selection
Pdf.AddLoadedComboBox(0, 72, 600, 300, 620, 'Country', 'Canada',
['United States', 'Canada', 'Mexico']);
// Scrolling list, no initial value
Pdf.AddLoadedListBox(0, 72, 520, 300, 590, 'Priority', '',
['Low', 'Normal', 'High']);
// Push button with a caption drawn through /MK
Pdf.AddLoadedPushButton(0, 360, 600, 480, 626, 'SubmitBtn', 'Submit');
Két megjegyzés a választási listához. A HotPDF minden /Opt bejegyzést egyszerű stringként ír ki, ahol az export érték és a megjelenített címke ugyanaz a szöveg. Az ISO 32000-1 §12.7.4.4 a kételemű [export display] alakot is megengedi, ha az elküldött értéknek el kell térnie attól, amit a felhasználó lát; a betöltött létrehozási metódusok az egyszerűbb, egyelemű formát használják, tehát ha eltérő export és megjelenítési értékre van szükséged, azt a létrejött szótárban kell beállítanod. Az a value, amelyet a mező aktuális kijelöléseként megadsz, legyen az egyik opció, mert a megjelenítő ehhez igazítja a választást
A push button a másik flagszabályozott eset: /Btn mezőtípus a 17. bit, a PushButton flag, értéke $10000. Ez a bit különbözteti meg a kattintható gombot a jelölőnégyzettől, amely szintén /Btn mező, csak éppen ez a bit nincs beállítva. A megadott felirat a megjelenési jellemzők szótárába, a /MK-ba kerül, mint a normál caption /CA. Ebben érdemes őszintének lenni a határokról: a gomb létrejön a feliratával és a téglalapjával együtt, de a betöltött létrehozási metódus nem csatol hozzá akciót, így önmagában egy olyan gombot kapsz, amely helyesen néz ki, de kattintásra semmit sem csinál. Submit, reset vagy JavaScript akciók összekötése külön kérdés; a nulláról szerkesztett oldalon az akciókkal együtt épített űrlapmunkafolyamatot a AcroForm mezők és akciók építése Delphiben című cikk mutatja be, és az a helyes összehasonlítási pont arra, amit a betöltött út szándékosan kihagy
Az a közös szótár, amelyet minden mező használ
A hat metódus alatt ugyanaz a közös builder dolgozik, amely létrehozza a widget annotációt és két helyre regisztrálja azt. Kiírja a /Type /Annot és /Subtype /Widget bejegyzéseket, a /Rect tömböt a négy koordinátádból, az annotációflagszámot /F 4-gyel, amely beállítja a Print bitet, hogy a mező papíron is megjelenjen, a mező nevét /T-ként, a mezőtípust /FT-ként, a flagszámot /Ff-ként, valamint egy /P visszahivatkozást az oldalobjektumra. Ezután az új mezőt hozzáadja az AcroForm /Fields tömbjéhez és az adott oldal /Annots tömbjéhez is, az indirekt hivatkozásokat feloldva, hogy a valódi tömböket bővítse, ne pedig árván hagyja a widgetet
Ez a kettős regisztráció azért fontos, mert egy olyan widget, amely csak az egyik listában él, finoman hibás. Ha a mező benne van a /Fields tömbben, de hiányzik az oldal /Annots listájából, akkor az űrlap ismeri, de soha nem rajzolódik ki; a fordított esetben megjelenik, de az űrlap logikája nem látja. A HotPDF minden hozzáadásnál mindkettőt szinkronban tartja, és ez az a fajta adminisztráció, amelyet egyébként neked kellene tökéletesen kézzel eltalálnod a specifikáció alapján
Néhány őszinte korlát
Állítsd be a várakozásokat, mielőtt erre workflow-t építesz. A flatten-and-regenerate viselkedés azon múlik, hogy a megjelenítő tiszteletben tartja-e a /NeedAppearances értéket, ami lefedi az Acrobatot, a modern böngészős PDF motorokat és a gyakori asztali olvasókat, de nem jelent kemény garanciát minden létező rendereren. Ha olyan fájlt kell előállítanod, amelynek a mezői mindenhol ugyanúgy jelennek meg, még olyan megjelenítőkben is, amelyek figyelmen kívül hagyják a flaget, akkor már appearance-stream területen vagy, és a nulláról épített, /AP-t is előállító út a jobb választás. Az aláírásmező ugyanígy üres signature widgetként jön létre, készen a későbbi aláírásra; a mező elhelyezése nem ugyanaz, mint egy kriptográfiai aláírás alkalmazása
Ami a már meglévő tartalom módosítását illeti, a rokon művelet az űrlap flattenelése, amikor az interaktív mezőket visszaégeted a statikus oldaltartalomba, hogy az értékek véglegessé és nem szerkeszthetővé váljanak; ezt a körforgást, beleértve azt is, hogyan kezeli a rendszer az XFA-t hordozó űrlapokat, a XFA és AcroForm mezők flattenelése Delphiben című cikk tárgyalja. A mezők hozzáadása és a mezők flattenelése ugyanannak az életciklusnak a két vége: ez a cikk azt mutatja meg, hogyan teszel interaktivitást egy olyan dokumentumba, amelyből hiányzott, a flattenelés pedig azt, hogyan veszed le róla, amikor az űrlap már betöltötte a szerepét
Az itt bemutatott betöltött dokumentum űrlap-API a szokásos HotPDF Component részeként érkezik Delphire és C++Builderre, a mezőflagszámok, a megjelenéskezelés és az AcroForm modell többi részének teljes referenciájával együtt