PDFlibPas dáva vývojárom v Delphi a C++Builder tri druhy akcií pre navigáciu, ktorá opúšťa aktuálnu stranu: GoToR (Go To Remote) otvorí konkrétnu stranu v inom PDF súbore, GoToE (Go To Embedded) otvorí PDF súbor vložený vnútri aktuálneho dokumentu a Launch spustí externý program alebo otvorí súbor cez shell operačného systému. Všetky tri žijú v ISO 32000-1 §12.6.4, sekcii Action Types, ktorá definuje aj bežnú akciu GoTo, a každá z nich nesie vlastnú pascu pre nepozorného: číslo strany, ktoré znamená niečo iné podľa toho, ktoré volanie ho zostavuje, cieľ, ktorý je názvom, nie cestou k súboru, a dvojicu reťazcových parametrov, ktoré vyzerajú identicky, ale slúžia dvom odlišným prehliadačom
Nič z toho nie je hypotetické. Balík technickej dokumentácie – hlavný manuál, PDF so špecifikáciami, ktoré distribútor aktualizuje podľa vlastného harmonogramu, kalibračný nástroj nainštalovaný popri oboch – sa opiera presne o takéto medzidokumentové prepojenie: krížový odkaz, ktorý musí pristáť na strane 5 súboru so špecifikáciami, dátový list, ktorý sa oplatí odoslať vnútri manuálu namiesto vedľa neho, odkaz, ktorý odovzdáva priamo ku kalibračnému nástroju. Tento článok je zrkadlovým obrazom článku čítanie akcií záložiek a anotácií späť z existujúceho PDF: ten pokrýva konzumáciu akcie GoToR, Launch alebo GoToE, ktorú do súboru už zapísal nejaký iný producent; tento pokrýva zostavovanie tých istých troch druhov akcií od základu, vrátane pravidiel na úrovni polí, ktoré PDFlibPas presadzuje ešte pred zápisom čo i len jedného bajtu
Tri spôsoby, ako akcia PDF môže opustiť aktuálnu stranu
PDFlibPas oddeľuje lokálnu navigáciu od všetkého ostatného na kľúči akcie /S, a GoToR, GoToE a Launch sú tri podtypy, ktorých cieľ leží mimo aktuálnej strany: GoToR podľa ISO 32000-1 §12.6.4.3, GoToE podľa §12.6.4.4 a Launch podľa §12.6.4.5, všetky vnútri širšej sekcie §12.6.4 Action Types, ktorá definuje aj bežnú akciu GoTo. Cieľ obyčajnej akcie GoTo pomenúva objekt strany, ktorý už vnútri dokumentu existuje, takže PDFlibPas ho môže overiť okamžite; GoToR a GoToE to tak urobiť nemôžu, keďže externý súbor nemusí na tomto počítači vôbec existovať a počet strán vloženého súboru nie je niečo, čo hostiteľský dokument sleduje, takže obe namiesto pevného odkazu nesú nevyriešenú referenciu – špecifikáciu súboru plus cieľ pre GoToR, názov vloženého súboru plus cieľovú stranu pre GoToE – zatiaľ čo Launch koncept cieľa úplne vypúšťa a jednoducho pomenúva niečo, čo má operačný systém spustiť alebo otvoriť. Toto rozdelenie sa na zápisovej strane prejavuje ako dve rodiny volaní: vysokoúrovňové, jednorazové generátory ako AddLinkToFile, AddLinkToFileEx, AddLinkToEmbeddedPDF a AddLinkToLocalFile vytvárajú spolu odkazovú anotáciu horúceho miesta na strane aj jej akciu, čo pokrýva väčšinu skutočných rozložení – riadok textu alebo ikonu, na ktorú čitateľ klikne – zatiaľ čo nižšie úrovňové nastavovače ako SetActionRemoteDestinationEx, SetActionLaunchOptions a ich náprotivky AddActionNext* pripájajú alebo nahrádzajú akciu na niečom, k čomu už máte handle: existujúca záložka, spúšťač poľa formulára, alebo udalosť životného cyklu na úrovni dokumentu či strany. Obe rodiny nakoniec zapisujú rovnaké tvary slovníkov; rozdiel je v tom, kde stojíte, keď ich voláte, a, ako pokrýva nasledujúca sekcia, čo znamená číslo strany, keď to robíte
Ako zostaviť odkaz GoToR, ktorý otvorí stranu v inom PDF súbore?
Akcia GoToR potrebuje dve veci – špecifikáciu súboru a cieľ vnútri tohto súboru – a PDFlibPas vystavuje dve odlišné volania na dodanie druhej časti, každé s vlastnou konvenciou číslovania strán. AddLinkToFile a AddLinkToFileEx, vysokoúrovňové generátory odkazových horúcich miest, overujú svoj argument Page alebo DestPage ako väčší než nula, rovnaké číslovanie od jednotky, aké PDFlibPas používa všade inde, vrátane SelectPage. SetActionRemoteDestinationEx, nižšie úrovňový nastavovač používaný na pripojenie alebo nahradenie akcie GoToR na niečom, k čomu už máte handle, namiesto toho overuje DestPage ako väčší alebo rovný nule a zapisuje ho priamo do poľa explicitného cieľa akcie bez akejkoľvek úpravy: chce surový, od nuly číslovaný index strany cieľového dokumentu, číslovanie, aké ISO 32000-1 predpisuje pre vzdialený explicitný cieľ. Zavolajte nižšie úrovňový nastavovač s tým istým číslom, aké by ste podali vysokoúrovňovému generátoru, a odkaz sa otvorí o jednu stranu skôr
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;
Zvyšok argumentov SetActionRemoteDestinationEx je rovnako doslovný. ValueMask je bitová množina – 1 pre ľavý okraj, 2 pre horný, 4 pre pravý, 8 pre dolný, 16 pre priblíženie – a PDFlibPas ju skontroluje voči DestType ešte pred akýmkoľvek zápisom: cieľ dkFitR musí dodať presne 15 (všetky štyri okraje, žiadne priblíženie), dkFit a dkFitB musia dodať 0 a dkFitH/dkFitV prijímajú len svoju jednu relevantnú súradnicu. Bity, ktoré ponecháte v inak platnej maske nenastavené, sa z poľa nevynechajú; zapíšu sa ako explicitný PDF null, čo ISO 32000-1 vníma ako „ponechaj hodnotu, akú prehliadač už pre danú súradnicu má“ – legitímny spôsob, ako povedať „skoč na túto stranu, priblíženie nechaj tak“, nie prehliadnutie. Samotné priblíženie sa ukladá ako zlomok odovzdanej hodnoty, takže volanie žiadajúce 150 percent podá poľu uloženú hodnotu 1,5, a platný vstupný rozsah je 0 až 6400
Ako prepojiť na PDF vložené vnútri vlastného dokumentu?
AddLinkToEmbeddedPDF zostavuje akciu GoToE a jej cieľový argument, EmbeddedFileName, je názov, nie cesta: musí sa zhodovať s reťazcom Title, ktorý už bol odovzdaný EmbedFile pri vytváraní prílohy, pretože tento titul je doslovný kľúč, ktorý PDFlibPas ukladá do stromu názvov /EmbeddedFiles dokumentu, a GoToE sa rieši vyhľadaním tohto názvu, nie opätovným dotykom súborového systému. Funkcia len skontroluje, že EmbeddedFileName nie je prázdny a TargetPage je aspoň 1 – podajte jej názov, ktorý nikdy nebol skutočne vložený, a volanie aj tak vráti úspech, akcia sa aj tak zapíše, a odkaz jednoducho zlyhá vyriešiť sa pre každého čitateľa, ktorý naň 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 sa vrství nie jedna, ale dve minimálne verzie. EmbedFile potrebuje PDF 1.4 pre strom názvov /EmbeddedFiles, a AddLinkToEmbeddedPDF samostatne zvyšuje dolnú hranicu na PDF 1.6 pre samotný druh akcie GoToE, takže efektívne minimum pre akýkoľvek dokument používajúci túto funkciu je 1.6, nie 1.4. Všimnite si tiež, že TargetPage je tu číslovaný od jednotky, obvyklá konvencia PDFlibPas – zámerný kontrast voči od nuly číslovanému DestPage, ktorý pokrývala predchádzajúca sekcia, a pripomienka, že to, ktorá schéma číslovania strán platí, závisí od druhu akcie a konkrétneho volania, nie od jedného plošného pravidla. Cieľový slovník akcie môže tiež niesť záznam /R s hodnotou C pre dieťa alebo P pre rodiča, čo podporuje dvojkrokový reťazec do vloženého súboru alebo späť von do jeho kontajnera, hoci AddLinkToEmbeddedPDF vždy zostavuje len smer k dieťaťu, keďže to je ten, ktorý má zmysel z dokumentu, ktorý vkladá, nie ktorý je vkladaný
Akcie Launch: jeden FileName, dva reťazcové ciele, ktoré nie sú zameniteľné
SetActionLaunchOptions zapisuje cieľ súboru akcie Launch do dvoch odlišných kľúčov z jediného argumentu FileName, a tieto dva kľúče nesú dva odlišné druhy reťazca. Kľúč najvyššej úrovne /F dostane slovník špecifikácie súboru, zostavený cez rovnakú konverziu ciest, akú PDFlibPas používa pre GoToR, čo je prenositeľná forma, ktorú ISO 32000-1 §7.11.3 definuje pre slovník špecifikácie súboru. Podslovník /Win, keď ho PDFlibPas zapíše, dostane vlastný kľúč /F nastavený na surovú hodnotu FileName presne tak, ako bola odovzdaná, bez akejkoľvek konverzie, pretože /Win /F je v ISO 32000-1 §12.6.4.5 zdokumentovaný ako obyčajný reťazec cesty Windows určený na čítanie len prehliadačom pre Windows. Odovzdajte prenositeľnú, už skonvertovanú cestu s očakávaním, že oba kľúče skončia identické, a kópia /Win bude niesť presne to, čo ste funkcii podali, nedotknuté
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;
Zaobchádzajte s Launch ako s najnáročnejšou z troch akcií, pretože jej celým zmyslom je spustiť program alebo otvoriť súbor mimo sandboxu PDF, a každý bežný prehliadač s ňou takto aj zaobchádza. Vylepšená bezpečnosť Adobe Acrobatu predvolene blokuje alebo sa pýta pri akciách Launch, pokiaľ cieľ nesedí na výslovne dôveryhodnom mieste, a väčšina firemných nasadení Acrobatu ponecháva túto ochranu zapnutú. Akcia Launch v dokumente odovzdanom verejnosti preto nie je spoľahlivým spúšťačom: počítajte s tým, že bude zablokovaná, vyžiada si potvrdenie alebo ju prehliadač, ktorý súbor otvorí, potichu ignoruje, a vyhraďte ju pre uzavreté prostredia, kde zároveň riadite aj nastavenia dôvery prehliadača – interný kiosk, riadené firemné nasadenie, dokument, ktorý nikdy neopustí počítač, ktorý spravujete
Brána PDF/A: prečo môžu volania GoToR a Launch vrátiť nulu
SetActionRemoteDestinationEx a SetActionLaunchOptions obe rovno odmietnu, keď je cieľový dokument v ktoromkoľvek režime zhody s PDF/A: obe kontrolujú režim PDF/A dokumentu ako svoju úplne prvú podmienku a skončia s výsledkom 0 ešte pred akýmkoľvek dotykom akcie, bez vyvolania výnimky. Toto je zámerné. Obmedzenia PDF/A na interaktívne akcie konkrétne vylučujú Launch, keďže dať archívnemu súboru schopnosť spustiť ľubovoľný program je presne ten druh správania závislého od prostredia, ktorému majú formáty na dlhodobú archiváciu zabrániť, a PDFlibPas uplatňuje rovnakú konzervatívnu bránu aj na nastavovač vzdialeného cieľa v tej istej ceste kódu. Praktický dôsledok je ľahké prehliadnuť počas vývoja: identické volanie, ktoré funguje na bežnom PDF, sa skompiluje, spustí a potichu nič neurobí na dokumente s nastavenou úrovňou zhody PDF/A, takže kontrolujte návratovú hodnotu namiesto predpokladania úspechu – 0 tu nie je chyba chybného vstupu, je to knižnica odmietajúca požiadavku, ktorá je v konflikte s vlastnou deklarovanou zhodou dokumentu
Kam GoToR, GoToE a Launch zapadajú do širšieho pracovného postupu PDFlibPas
Tri druhy akcií v tomto článku nedosahujú všetky rovnaké miesta. Sprievodný článok o spúšťačoch akcií životného cyklu dokumentu a strany pokrýva SetDocumentAction a SetPageAction, ktoré môžu pripojiť akciu GoToR alebo Launch k spúšťaču ako WillClose cez zdieľané konštanty PDF_ACTION_BUILDER_REMOTE_DESTINATION a PDF_ACTION_BUILDER_LAUNCH – ten istý generátor, ktorý pokrýva aj obyčajný spúšťač URI alebo JavaScript. GoToE takúto konštantu vôbec nemá a nemá ani žiadnu cestu do tohto všeobecného generátora; AddLinkToEmbeddedPDF je jediný spôsob, akým PDFlibPas takúto akciu zostavuje, čo z nej robí striktne akciu horúceho miesta na strane, nikdy spúšťač na úrovni dokumentu či strany. Tam, kde GoToR a Launch do všeobecného generátora naozaj dosahujú, kompromisom je kontrola: zostaví GoToR smerujúci len na pomenovaný vzdialený cieľ a akciu Launch len s názvom súboru a parametrami, zatiaľ čo explicitné adresovanie podľa strany a typu prispôsobenia zobrazenia a špecifické možnosti spúšťania pre Windows opísané v tomto článku sú dosiahnuteľné len priamo cez SetActionRemoteDestinationEx a SetActionLaunchOptions
Jednu bezpečnostnú vlastnosť sa oplatí poznať pred zostavovaním údržbového nástroja okolo týchto nastavovačov. SetActionRemoteDestinationEx a SetActionLaunchOptions zostavia celú náhradnú akciu najprv v pomocnom slovníku a kľúče /F, /D alebo /Win, a /NewWindow odstránia a skopírujú na živú akciu len po tom, čo sa táto pomocná kópia overí – takže volanie, ktoré zlyhá na validácii, či už kvôli ValueMask mimo rozsah alebo prázdnemu FileName, ponechá pôvodnú akciu, a akýkoľvek reťazec /Next už na nej visiaci, úplne nedotknuté namiesto napoly prepísané. Na tom záleží, pretože akcie GoToR aj Launch môžu sedieť vnútri reťazca /Next zostaveného pomocou AddActionNextRemoteDestinationEx, AddActionNextLaunchEx, alebo všeobecnejšieho AddActionNextEx, čo umožňuje, aby jediný spúšťač postupne odpálil záznam JavaScript logu a potom vzdialený skok. Zostavovanie GoToR, GoToE a Launch, ako je opísané tu, je súčasťou PDFlibPas, natívnej PDF knižnice pre Delphi a C++Builder