PDFlibPas razvijalcem Delphi in C++Builder ponuja tri vrste dejanj za navigacijo, ki zapusti trenutno stran: GoToR (Go To Remote) odpre določeno stran v drugi datoteki PDF, GoToE (Go To Embedded) odpre datoteko PDF, vdelano v trenutni dokument, Launch pa zažene zunanji program ali odpre datoteko prek lupine operacijskega sistema. Vse tri vrste so opisane v ISO 32000-1 §12.6.4, razdelku Vrste dejanj, ki določa tudi običajno dejanje GoTo, vsaka pa skriva svojo past za neprevidne: številko strani, ki pomeni nekaj drugega glede na klic, ki jo ustvari, cilj, ki je ime in ne pot datoteke, ter dva niza parametrov, ki sta videti enaka, vendar ju različna pregledovalnika uporabljata različno
Nič od tega ni hipotetično. Paket tehničnih priročnikov — glavni priročnik, PDF s specifikacijami, ki ga distributer posodablja po svojem urniku, in orodje za umerjanje, nameščeno poleg obeh — se opira natanko na takšno povezovanje med dokumenti: navzkrižno sklicevanje, ki mora pristati na strani 5 datoteke s specifikacijami, podatkovni list, ki ga je bolje poslati znotraj priročnika kot poleg njega, in povezavo, ki neposredno preda izvajanje orodju za umerjanje. Ta članek je zrcalna slika članka o branju dejanj zaznamkov in opomb iz obstoječega PDF-ja: tisti del obravnava uporabo dejanja GoToR, Launch ali GoToE, ki ga je v datoteko že zapisal drug izdelovalec, ta pa obravnava izdelavo istih treh vrst dejanj iz nič, vključno s pravili na ravni polj, ki jih PDFlibPas uveljavi, preden zapiše en sam bajt
Trije načini, kako dejanje PDF zapusti trenutno stran
PDFlibPas loči lokalno navigacijo od vsega drugega na ključu dejanja /S, GoToR, GoToE in Launch pa so tri podvrste, katerih cilj je zunaj trenutne strani: GoToR pod ISO 32000-1 §12.6.4.3, GoToE pod §12.6.4.4 in Launch pod §12.6.4.5, vse znotraj širšega razdelka §12.6.4 Vrste dejanj, ki določa tudi običajno dejanje GoTo. Cilj navadnega dejanja GoTo poimenuje objekt strani, ki že obstaja v dokumentu, zato ga lahko PDFlibPas takoj preveri; GoToR in GoToE tega ne moreta storiti na enak način, saj zunanja datoteka morda sploh ne obstaja na tem računalniku, število strani vdelane datoteke pa ni nekaj, kar bi gostiteljski dokument spremljal, zato oba nosita nerešeno sklicevanje namesto trde povezave — specifikacijo datoteke in cilj za GoToR, ime vdelane datoteke in ciljno stran za GoToE — medtem ko Launch pojem cilja v celoti opusti in poimenuje le nekaj, kar naj operacijski sistem zažene ali odpre. Ta ločitev se na strani zapisovanja pokaže kot dve družini klicev: visokoravenski gradniki v enem koraku, kot so AddLinkToFile, AddLinkToFileEx, AddLinkToEmbeddedPDF in AddLinkToLocalFile, ustvarijo opombo povezave z vročo točko na strani in njeno dejanje hkrati ter pokrijejo večino resničnih postavitev — vrstico besedila ali ikono, ki jo bralec klikne — medtem ko nižjeravenski nastavljalniki, kot so SetActionRemoteDestinationEx, SetActionLaunchOptions in njihove ustrezne različice AddActionNext*, pripnejo ali zamenjajo dejanje na nečem, za kar že imate ročaj: obstoječem zaznamku, sprožilcu polja obrazca ali dogodku življenjskega cikla na ravni dokumenta ali strani. Obe družini na koncu zapišeta enake oblike slovarjev; razlika je v tem, kje ste, ko ju pokličete, in, kot pojasnjuje naslednji razdelek, kaj pomeni številka strani, ko to storite
Kako izdelate povezavo GoToR, ki odpre stran v drugi datoteki PDF
Dejanje GoToR potrebuje dve stvari — specifikacijo datoteke in cilj znotraj te datoteke — PDFlibPas pa ponuja dva različna klica za podajanje drugega dela, vsak s svojim pravilom oštevilčenja strani. AddLinkToFile in AddLinkToFileEx, visokoravenska gradnika vročih točk na strani, preverita argument Page ali DestPage, ali je večji od nič, kar je enako 1-osnovanemu oštevilčenju, ki ga PDFlibPas uporablja povsod drugje, tudi pri SelectPage. SetActionRemoteDestinationEx, nižjeravenski nastavljalnik za pripenjanje ali zamenjavo dejanja GoToR na nečem, za kar že imate ročaj, pa preveri DestPage, ali je večji ali enak nič, in ga brez prilagoditve zapiše neposredno v izrecno ciljno matriko dejanja: pričakuje surovi 0-osnovani indeks strani ciljnega dokumenta, kot ga za oddaljeni izrecni cilj določa ISO 32000-1. Če nižjeravenski nastavljalnik pokličete z isto številko, ki bi jo predali visokoravenskemu gradniku, se povezava odpre eno stran prezgodaj
var
Lib: TPDFlib;
ActionID: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile('manual.pdf', '') = 1 then
begin
Lib.SelectPage(12);
// Page is 1-based here, same as SelectPage above: this opens
// the fifth page of specs.pdf.
Lib.AddLinkToFile(72, 700, 200, 16, 'specs.pdf', 5, 0, 0, 0);
// A later maintenance pass repoints the same link at a
// reorganized file. SetActionRemoteDestinationEx edits the
// action directly, and DestPage here is the zero-based index
// PDF itself uses for a remote explicit destination -- "the
// fifth page" is now 4, not 5.
ActionID := Lib.GetAnnotActionID(1);
Lib.SetActionRemoteDestinationEx(ActionID, 'specs-2026.pdf',
4, Ord(dkFit), 0, 0, 0, 0, 0, 0, -1);
end;
finally
Lib.Free;
end;
end;
Preostali argumenti SetActionRemoteDestinationEx so prav tako dobesedni. ValueMask je bitna množica — 1 za levo, 2 za vrh, 4 za desno, 8 za dno in 16 za povečavo — PDFlibPas pa jo pred zapisom preveri glede na DestType: cilj dkFitR mora podati natanko 15 (vse štiri robove brez povečave), dkFit in dkFitB morata podati 0, dkFitH/dkFitV pa sprejmeta samo svojo ustrezno koordinato. Biti, ki jih znotraj sicer veljavne maske pustite neizbrane, niso izpuščeni iz matrike; zapisani so kot izrecna ničelna vrednost PDF, ki jo ISO 32000-1 obravnava kot »ohrani katero koli vrednost, ki jo pregledovalnik že ima« za to koordinato — legitimen način, da rečete »skoči na to stran, povečavo pa pusti pri miru«, ne pa spregled. Sama povečava je shranjena kot ulomek posredovane vrednosti, zato klic, ki zahteva 150 odstotkov, v matriko posreduje shranjeno vrednost 1,5, veljavno vhodno območje pa je od 0 do 6400
Kako se povežete s PDF-jem, vdelanim v lasten dokument
AddLinkToEmbeddedPDF izdela dejanje GoToE, njegov ciljni argument EmbeddedFileName pa je ime in ne pot: ujemati se mora z nizom Title, ki ste ga že posredovali funkciji EmbedFile ob dodajanju priponke, saj je ta naslov dobesedni ključ, ki ga PDFlibPas shrani v drevo imen dokumenta /EmbeddedFiles, GoToE pa razrešuje z iskanjem tega imena in ne z novim dostopanjem do datotečnega sistema. Funkcija preveri le, ali EmbeddedFileName ni prazen in ali je TargetPage vsaj 1 — če posredujete ime, ki dejansko ni bilo nikoli vdelano, klic še vedno vrne uspeh, dejanje se še vedno zapiše, povezava pa se preprosto ne bo razrešila za nobenega bralca, ki jo klikne
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.NewPage;
// The Title argument becomes the key PDFlibPas stores in the
// document's EmbeddedFiles name tree -- that string, not
// "datasheet.pdf", is the target GoToE resolves against.
if Lib.EmbedFile('Datasheet', 'datasheet.pdf', 'application/pdf') = 1 then
Lib.AddLinkToEmbeddedPDF(72, 700, 200, 16, 'Datasheet', 3, 0, 0);
Lib.SaveToFile('manual.pdf');
finally
Lib.Free;
end;
end;
Tu se seštejeta dve različici, ne ena. EmbedFile potrebuje PDF 1.4 za drevo imen /EmbeddedFiles, AddLinkToEmbeddedPDF pa ločeno dvigne najmanjšo različico na PDF 1.6 za samo vrsto dejanja GoToE, zato je dejanski minimum za vsak dokument, ki uporablja to funkcijo, 1.6 in ne 1.4. Upoštevajte tudi, da je TargetPage tukaj 1-osnovan, kar je običajna konvencija PDFlibPas — nameren kontrast z 0-osnovanim DestPage, obravnavanim v prejšnjem razdelku, in opomnik, da je uporabljena shema oštevilčenja strani odvisna od vrste dejanja in konkretnega klica, ne od enotnega pravila. Ciljni slovar dejanja lahko vsebuje tudi vnos /R z vrednostjo C za otroka ali P za nadrejeni dokument, kar podpira dvokoračno verigo v vdelano datoteko ali nazaj v vsebnik, vendar AddLinkToEmbeddedPDF izdela samo smer proti otroku, saj je to edina smiselna smer iz dokumenta, ki vdeluje datoteko, in ne iz dokumenta, ki je vdelan
Dejanja Launch: eno ime FileName, dva ciljna niza, ki nista zamenljiva
SetActionLaunchOptions zapiše cilj datoteke dejanja Launch v dva različna ključa iz enega argumenta FileName, ključa pa vsebujeta dve različni vrsti niza. Vrhnji ključ /F dobi slovar specifikacije datoteke, izdelan z isto pretvorbo poti, kot jo PDFlibPas uporablja za GoToR, kar je prenosljiva oblika, ki jo ISO 32000-1 §7.11.3 določa za slovar specifikacije datoteke. Podslovar /Win, kadar ga PDFlibPas zapiše, dobi lasten ključ /F, nastavljen na surovo vrednost FileName, natančno tako, kot je bila posredovana, brez kakršne koli pretvorbe, saj je /Win /F v ISO 32000-1 §12.6.4.5 dokumentiran kot navaden niz poti Windows, namenjen samo branju v pregledovalniku Windows. Če posredujete prenosljivo, že pretvorjeno pot in pričakujete, da bosta oba ključa enaka, bo kopija /Win vsebovala točno tisto, kar ste predali funkciji, brez sprememb
var
Lib: TPDFlib;
ActionID: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile('manual.pdf', '') = 1 then
begin
Lib.SelectPage(1);
Lib.AddLinkToLocalFile(72, 660, 220, 16, 'calibrate.exe', 0);
ActionID := Lib.GetAnnotActionID(1);
// Operation 0 leaves this as a normal open -- pass 1 to ask a
// Windows viewer to print instead. Parameters and
// DefaultDirectory only ever reach /Win /P and /Win /D, never
// the top-level /F.
Lib.SetActionLaunchOptions(ActionID, 'calibrate.exe',
'/silent /profile:default', 'C:\Tools\Calibration', 0, -1);
end;
finally
Lib.Free;
end;
end;
Launch obravnavajte kot dejanje z največ trenja med temi tremi, saj je njegov celotni namen zagon programa ali odpiranje datoteke zunaj peskovnika PDF, zato ga vsak običajni pregledovalnik obravnava temu primerno. Izboljšana varnost Adobe Acrobat privzeto blokira dejanja Launch ali prikaže poziv, razen če cilj leži na izrecno zaupanja vrednem mestu, večina poslovnih namestitev Acrobat pa to zaščito ohrani vključeno. Dejanje Launch v dokumentu, namenjenem javnosti, zato ni zanesljiv sprožilec: računajte na to, da ga bo pregledovalnik, ki odpre datoteko, blokiral, zahteval potrditev ali tiho prezrl, zato ga prihranite za zaprta okolja, v katerih nadzorujete tudi nastavitve zaupanja pregledovalnika — notranji kiosk, nadzorovano uvajanje v podjetju ali dokument, ki nikoli ne zapusti računalnika pod vašim upravljanjem
Omejitev PDF/A: zakaj lahko klica GoToR in Launch vrneta nič
SetActionRemoteDestinationEx in SetActionLaunchOptions oba brezpogojno zavrneta zahtevo, kadar je ciljni dokument v katerem koli načinu skladnosti PDF/A: oba najprej preverita način PDF/A dokumenta in končata z rezultatom 0, preden se dotakneta dejanja, brez sprožene izjeme. To je namerno. Omejitve PDF/A glede interaktivnih dejanj izrecno izključujejo Launch, saj je možnost, da arhivska datoteka zažene poljuben program, natanko vrsta od okolja odvisnega vedenja, ki ga želijo formati za dolgoročno arhiviranje preprečiti, PDFlibPas pa enako previdno omejitev uporabi tudi za nastavljalnik oddaljenega skoka po isti poti kode. Praktično posledico je med razvojem zlahka spregledati: isti klic, ki deluje pri običajnem PDF-ju, se bo pri dokumentu z nastavljeno ravnjo skladnosti PDF/A prevedel, izvedel in tiho ne storil ničesar, zato preverite vrnjeno vrednost, namesto da bi domnevali uspeh — 0 tukaj ni napaka nepravilnega vnosa, temveč knjižnica zavrne zahtevo, ki je v nasprotju z lastno trditvijo dokumenta o skladnosti
Kam se GoToR, GoToE in Launch vključijo v večji potek dela PDFlibPas
Tri vrste dejanj iz tega članka ne dosežejo vseh enakih mest. Spremljevalni članek o sprožilcih življenjskega cikla dokumenta in strani obravnava SetDocumentAction in SetPageAction, ki lahko dejanju GoToR ali Launch pripneta sprožilec, kot je WillClose, prek skupnih konstant PDF_ACTION_BUILDER_REMOTE_DESTINATION in PDF_ACTION_BUILDER_LAUNCH — istega gradnika, ki pokriva tudi navaden sprožilec URI ali JavaScript. GoToE nima takšne konstante in nima nobene poti v ta splošni gradnik; AddLinkToEmbeddedPDF je edini način, na katerega PDFlibPas izdela GoToE, zato je strogo dejanje vroče točke na strani in nikoli sprožilec na ravni dokumenta ali strani. Kjer GoToR in Launch dosežeta splošni gradnik, je kompromis nadzor: ta izdela GoToR, ki kaže samo na poimenovani oddaljeni cilj, in dejanje Launch samo z imenom datoteke in parametri, medtem ko sta izrecno naslavljanje strani in vrste prilagajanja ter možnosti zagona, značilne za Windows, opisane v tem članku dostopna samo neposredno prek SetActionRemoteDestinationEx in SetActionLaunchOptions
Pred izdelavo vzdrževalnega orodja na osnovi teh nastavljalnikov je vredno poznati eno varnostno lastnost. SetActionRemoteDestinationEx in SetActionLaunchOptions najprej izdelata celotno nadomestno dejanje v pomožnem slovarju, ključe /F, /D ali /Win ter /NewWindow pa na dejanje v živo izbrišeta in kopirata šele, ko ta pomožna kopija prestane preverjanje — zato klic, ki ne uspe pri preverjanju, bodisi zaradi neveljavne ValueMask bodisi praznega FileName, pusti prvotno dejanje in vsako verigo /Next, ki že visi na njem, popolnoma nespremenjena, namesto da bi jo delno prepisal. To je pomembno, ker sta dejanji GoToR in Launch lahko obe znotraj verige /Next, izdelane z AddActionNextRemoteDestinationEx, AddActionNextLaunchEx ali splošnejšim AddActionNextEx, kar omogoča, da en sprožilec zaporedoma izvede vnos JavaScript za beleženje in nato oddaljeni skok. Izdelava dejanj GoToR, GoToE in Launch, opisana tukaj, je del PDFlibPas, izvorne knjižnice PDF za Delphi in C++Builder