Műszaki cikk

GoToR-, GoToE-, és Launch-akciók Delphi PDF-ekben

A PDF Library for Delphi három akciótípust ad a Delphi- és C++Builder-fejlesztőknek olyan navigációhoz, amely elhagyja az aktuális oldalt: a GoToR (Go To Remote) egy konkrét oldalt nyit meg egy másik PDF-fájlban, a GoToE (Go To Embedded) egy PDF-fájlt nyit meg, amely az aktuális dokumentumon belülre van ágyazva, és a Launch egy külső programot futtat, vagy egy fájlt nyit meg az operációs rendszer héján keresztül. Mindhárom az ISO 32000-1 §12.6.4-ben él, az Action Types szakaszban, amely a hétköznapi GoTo akciót is definiálja, és mindegyik saját csapdát hordoz a nem gyanútlanoknak: egy oldalszám, amely mást jelent attól függően, melyik hívás építi, egy cél, amely egy név, nem egy fájlútvonal, és egy sztring-paraméter-pár, amely azonosnak néz ki, de két különböző megjelenítőt szolgál

Ebből semmi nem hipotetikus. Egy technikai referenciacsomag — egy fő kézikönyv, egy specifikációk-PDF, amit egy forgalmazó a saját ütemezésén frissít, egy kalibrálási eszköz, mindkettő mellé telepítve — pontosan erre a fajta dokumentumok-közötti bekötésre támaszkodik: egy kereszthivatkozás, amelynek a specs-fájl 5. oldalán kell landolnia, egy adatlap, amit megéri a kézikönyvön belülre szállítani, nem mellette, egy link, amely egyenesen átadja a vezérlést a kalibráló eszköznek. Ez a cikk tükörképe a könyvjelző- és annotáció-akciók egy meglévő PDF-ből való visszaolvasásáról szóló cikknek: az egy darab egy GoToR, Launch, vagy GoToE akció fogyasztásáról szól, amit egy másik előállító már beírt egy fájlba; ez itt ugyanennek a három akciótípusnak nulláról történő felépítéséről szól, beleértve a mezőszintű szabályokat, amiket a PDF Library for Delphi érvényesít, mielőtt egyetlen bájtot is elkötelezne

Három mód, ahogy egy PDF-akció elhagyhatja az aktuális oldalt

A PDF Library for Delphi elválasztja a helyi navigációt mindentől mástól az akció /S kulcsán, és a GoToR, a GoToE, és a Launch a három altípus, amelyeknek célja az aktuális oldalon kívül ül: a GoToR az ISO 32000-1 §12.6.4.3 alatt, a GoToE a §12.6.4.4 alatt, és a Launch a §12.6.4.5 alatt, mindegyik a szélesebb §12.6.4 Action Types szakaszban, amely a hétköznapi GoTo akciót is definiálja. Egy egyszerű GoTo akció célja egy oldalobjektumot nevez meg, amely már létezik a dokumentumon belül, így a PDF Library for Delphi azonnal érvényesítheti; a GoToR és a GoToE nem tudja ezt ugyanúgy megtenni, mivel a külső fájl esetleg nem is létezik ezen a gépen, és egy beágyazott fájl oldalszáma nem olyasmi, amit a gazdadokumentum nyomon követ, így mindkettő egy feloldatlan hivatkozást hordoz kemény link helyett — egy fájlspecifikáció plusz egy célhely a GoToR-hoz, egy beágyazottfájl-név plusz egy céloldal a GoToE-hez —, míg a Launch teljesen elejti a célhely-fogalmat, és csak megnevez valamit, amit az operációs rendszernek futtatnia vagy meg kell nyitnia. Ez a felosztás két hívási családként mutatkozik meg az írási oldalon: magas-szintű, egyhívásos építők, mint az AddLinkToFile, az AddLinkToFileEx, az AddLinkToEmbeddedPDF, és az AddLinkToLocalFile, egy oldal-hotspot link-annotációt és annak akcióját együtt hozzák létre, lefedve a legtöbb valódi elrendezést — egy szövegsor vagy egy ikon, amire egy olvasó kattint —, míg alacsonyabb-szintű beállítók, mint a SetActionRemoteDestinationEx, a SetActionLaunchOptions, és az AddActionNext* megfelelőik, csatolnak vagy lecserélnek egy akciót valamin, aminek már birtokolod a handle-jét: egy meglévő könyvjelzőn, egy formmező-kiváltón, vagy egy dokumentum- vagy oldal-szintű élettartam-eseményen. Mindkét család ugyanazokat a szótáralakokat végzi el megírva; a különbség ott van, hol állsz, amikor meghívod őket, és, ahogy a következő szakasz tárgyalja, mit jelent egy oldalszám, amikor teszed

Hogyan építesz egy GoToR linket, amely egy oldalt nyit meg egy másik PDF-fájlban?

Egy GoToR akciónak két dologra van szüksége — egy fájlspecifikációra és egy célhelyre azon a fájlon belül —, és a PDF Library for Delphi két különböző hívást tesz elérhetővé a második rész szolgáltatásához, mindegyik saját oldalszámozási konvencióval. Az AddLinkToFile és az AddLinkToFileEx, a magas-szintű oldal-hotspot építők, érvényesítik Page vagy DestPage argumentumukat nullánál nagyobbként, ugyanaz az 1-alapú számozás, amit a PDF Library for Delphi mindenhol máshol használ, a SelectPage-et is beleértve. A SetActionRemoteDestinationEx, az alacsonyabb-szintű beállító, amit egy GoToR akció csatolására vagy cseréjére használnak valamin, aminek már van handle-je, ehelyett a DestPage-et nullánál nagyobb vagy egyenlőként érvényesíti, és egyenesen beírja azt az akció explicit célhely-tömbjébe kiigazítás nélkül: a céldokumentum nyers, nulla-alapú oldalindexét akarja, azt a számozást, amit az ISO 32000-1 meghatároz egy távoli explicit célhelyhez. Hívd meg az alacsony-szintű beállítót ugyanazzal a számmal, amit a magas-szintű építőnek adnál, és a link egy oldallal korábban nyílik meg

PDF Library for Delphi összehasonlítás: az AddLinkToFile egyalapú oldalszámozása a SetActionRemoteDestinationEx nullaalapú távoli célindexével szemben
Az AddLinkToFile az oldalargumentumát egyalapúként validálja, míg a SetActionRemoteDestinationEx a nulla alapú távoli indexet változatlanul írja. Ugyanaz a szám mindkét hívásnak átadva két különböző oldalt nyit meg
var
  Lib: TPDFlib;
  ActionID: Integer;
begin
  Lib := TPDFlib.Create;
  try
    if Lib.LoadFromFile('manual.pdf', '') = 1 then
    begin
      Lib.SelectPage(12);
      // A Page itt 1-alapú, ugyanúgy, mint a fenti SelectPage-nél: ez
      // a specs.pdf ötödik oldalát nyitja meg.
      Lib.AddLinkToFile(72, 700, 200, 16, 'specs.pdf', 5, 0, 0, 0);

      // Egy későbbi karbantartási lépés átirányítja ugyanazt a linket egy
      // átszervezett fájlra. A SetActionRemoteDestinationEx közvetlenül
      // szerkeszti az akciót, és a DestPage itt az a nulla-alapú index,
      // amit maga a PDF használ egy távoli explicit célhelyhez -- "az
      // ötödik oldal" most 4, nem 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;

A SetActionRemoteDestinationEx maradék argumentumai éppolyan szó szerintiek. A ValueMask egy bithalmaz — 1 a balhoz, 2 a felsőhöz, 4 a jobbhoz, 8 az alsóhoz, 16 a nagyításhoz —, és a PDF Library for Delphi ellenőrzi azt a DestType ellen, mielőtt bármit is megírna: egy dkFitR célhelynek pontosan 15-öt kell szolgáltatnia (mind a négy él, nincs nagyítás), a dkFit és a dkFitB 0-t kell szolgáltasson, és a dkFitH/dkFitV csak az egy releváns koordinátáját fogadja el. Bitek, amiket beállítatlanul hagysz egy egyébként érvényes masszkon belül, nincsenek kihagyva a tömbből; explicit PDF null-ként íródnak, amit az ISO 32000-1 "tartsd meg bármilyen értéket, amit a megjelenítő már tart" jelentéssel kezel az adott koordinátára — ez egy legitim mód arra, hogy "ugorj erre az oldalra, hagyd békén a nagyítást" mondj ahelyett hogy egy hiba lenne. Maga a nagyítás az általad átadott érték törtjeként tárolódik, így egy hívás, amely 150 százalékot kér, egy 1,5-ös tárolt értéket ad a tömbnek, és az érvényes bemeneti tartomány 0-tól 6400-ig terjed

Hogyan linkelsz egy PDF-hez, amely a saját dokumentumodon belül van beágyazva?

Az AddLinkToEmbeddedPDF építi fel a GoToE akciót, és céljának argumentuma, az EmbeddedFileName, egy név, nem egy útvonal: illeszkednie kell a Title sztringhez, amit már átadtak az EmbedFile-nak, amikor a mellékletet készítették, mert az a cím a szó szerinti kulcs, amit a PDF Library for Delphi a dokumentum /EmbeddedFiles névfájában tárol, és a GoToE azt a nevet keresve oldódik fel, nem a fájlrendszerhez nyúlva ismét. A függvény csak azt ellenőrzi, hogy az EmbeddedFileName nem üres, és a TargetPage legalább 1 — adj át egy nevet, amit soha nem ágyaztak be ténylegesen, és a hívás még mindig sikert ad vissza, az akció még mindig megíródik, és a link egyszerűen nem oldódik fel minden olvasónak, aki rákattint

PDF Library for Delphi: GoToE feloldási folyamat: egy hivatkozási forróponttól az EmbeddedFiles névfa Title kulcsain át a beágyazott PDF egy oldaláig
A GoToE a célját az EmbeddedFiles névfában tárolt Title egyeztetésével határozza meg, fájlrendszeri útvonal helyett. Egy páratlan név is ír egy műveletet, amely minden olvasó számára halott marad
var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.NewDocument;
    Lib.NewPage;
    // A Title argumentum lesz az a kulcs, amit a PDF Library for Delphi a
    // dokumentum EmbeddedFiles névfájában tárol -- ez a sztring, nem
    // a "datasheet.pdf", az a cél, amivel szemben a GoToE feloldódik.
    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;

Két verziópadló rétegződik itt, nem egy. Az EmbedFile-nak PDF 1.4-re van szüksége a /EmbeddedFiles névfához, és az AddLinkToEmbeddedPDF külön PDF 1.6-ra emeli a padlót magához a GoToE akciótípushoz, így a hatékony minimum bármely dokumentumhoz, amely ezt a funkciót használja, 1.6, nem 1.4. Vedd észre azt is, hogy a TargetPage itt 1-alapú, a szokásos PDF Library for Delphi-konvenció — szándékos kontrasztban a nulla-alapú DestPage-gyel, amit az előző szakasz épp tárgyalt, és emlékeztető, hogy melyik oldalszám-séma alkalmazandó, az akciótípustól és a konkrét hívástól függ, nem egy általános szabálytól. Az akció célszótára hordozhat egy /R bejegyzést is C-vel gyerekhez, vagy P-vel szülőhöz, támogatva egy kétugrásos láncot egy beágyazott fájlba, vagy vissza kifelé annak konténerébe, bár az AddLinkToEmbeddedPDF csak valaha a gyerek irányt építi, mivel ez az, aminek van értelme egy beágyazást végző dokumentumnál, nem egy beágyazottnál

Launch-akciók: egy FileName, két sztring-cél, amelyek nem cserélhetők fel

A SetActionLaunchOptions egy Launch akció fájlcélját két különböző kulcsra írja egyetlen FileName argumentumból, és a két kulcs kétfajta sztringet tart. A legfelső szintű /F kulcs egy fájlspecifikáció-szótárat kap, ugyanazon útvonal-konverzión keresztül építve, amit a PDF Library for Delphi a GoToR-hoz használ, ami a hordozható forma, amit az ISO 32000-1 §7.11.3 definiál egy fájlspecifikáció-szótárhoz. A /Win alszótár, amikor a PDF Library for Delphi ír egyet, saját /F kulcsot kap, a nyers FileName értékre állítva pontosan úgy, ahogy átadták, minden konverzió nélkül, mert a /Win /F az ISO 32000-1 §12.6.4.5-ben dokumentált egyszerű Windows útvonal-sztringként, csak egy Windows-megjelenítőnek szánva olvasásra. Adj át egy hordozható, már konvertált útvonalat, azt várva, hogy mindkét kulcs azonosan végez, és a /Win másolat azt fogja hordozni, amit átadtál a függvénynek, érintetlenül

PDF Library for Delphi: a Launch művelet egy FileName argumentumot ír hordozható /F fájlspecifikációba, valamint szó szerinti /Win alszótár-példányt, amely paramétereket és alapértelmezett könyvtárat tart
A SetActionLaunchOptions egyetlen FileName-t két különböző karakterlánc-célra ágaztat szét. A legfelső szintű /F kulcs útvonalkonverziót kap, míg a /Win nyers értéke érintetlen marad
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);
      // A 0-s Operation normál megnyitásként hagyja ezt -- adj át 1-et,
      // hogy egy Windows-megjelenítőtől nyomtatást kérj helyette. A Parameters
      // és a DefaultDirectory kizárólag a /Win /P és a /Win /D kulcsokba
      // jut el, sosem a legfelső szintű /F-be.
      Lib.SetActionLaunchOptions(ActionID, 'calibrate.exe',
        '/silent /profile:default', 'C:\Tools\Calibration', 0, -1);
    end;
  finally
    Lib.Free;
  end;
end;

Kezeld a Launch-ot a három közül a legmagasabb-súrlódásúnak, mert a teljes célja egy program futtatása vagy egy fájl megnyitása a PDF-homokozón kívül, és minden mainstream megjelenítő ennek megfelelően kezeli. Az Adobe Acrobat Enhanced Security-je alapértelmezetten blokkolja vagy megkérdezi a Launch-akciókat, hacsak a cél kifejezetten megbízható helyen nem ül, és a legtöbb vállalati Acrobat-telepítés bekapcsolva hagyja azt a védelmet. Egy Launch-akció egy nyilvánosságnak átadott dokumentumban tehát nem megbízható kiváltó: tervezz úgy, hogy blokkolják, megkérdezik, vagy csendben figyelmen kívül hagyja bármelyik megjelenítő, amely megnyitja a fájlt, és tartsd fenn zárt környezetekhez, ahol a megjelenítő bizalmi beállításait is te kontrollálod — egy belső kiosk, egy kontrollált vállalati kiépítés, egy dokumentum, amely soha nem hagyja el egy általad felügyelt gépet

A PDF/A-kapu: miért adhat vissza nullát a GoToR és a Launch hívás

A SetActionRemoteDestinationEx és a SetActionLaunchOptions mindkettő egyenesen megtagadja, amikor a céldokumentum bármilyen PDF/A megfelelőségi módban van: mindkettő ellenőrzi a dokumentum PDF/A módját mint első feltételét, és kilép 0 eredménnyel, mielőtt hozzányúlna az akcióhoz, kivétel dobása nélkül. Ez szándékos. A PDF/A korlátozásai az interaktív akciókra kifejezetten kizárják a Launch-ot, mivel egy archivális fájlnak megadni azt a képességet, hogy egy tetszőleges programot futtasson, pontosan az a fajta környezet-függő viselkedés, aminek megelőzésére a hosszútávú archiválási formátumok léteznek, és a PDF Library for Delphi ugyanazt a konzervatív kaput alkalmazza a távoli-célhely-beállítóra ugyanabban a kódútvonalban. A gyakorlati következmény könnyen elmulasztható fejlesztés közben: az azonos hívás, amely egy közönséges PDF-en működik, lefordul, fut, és csendben semmit nem tesz egy olyan dokumentumon, amelyet PDF/A megfelelőségi szinttel töltöttek be, így ellenőrizd a visszatérési értéket ahelyett hogy sikert feltételeznél — egy 0 itt nem egy hibás-formátumú-bemenet hiba, a könyvtár megtagad egy kérést, amely ütközik a dokumentum saját megfelelőségi állításával

Hol illik a GoToR, a GoToE, és a Launch egy nagyobb PDF Library for Delphi munkafolyamatba

A cikkben szereplő három akciótípus nem mind ugyanoda ér el. A dokumentum- és oldal-élettartam-akció-kiváltókról szóló kísérőcikk tárgyalja a SetDocumentAction-t és a SetPageAction-t, amelyek csatolhatnak egy GoToR vagy egy Launch akciót egy kiváltóhoz, mint a WillClose, a megosztott PDF_ACTION_BUILDER_REMOTE_DESTINATION és PDF_ACTION_BUILDER_LAUNCH konstansokon keresztül — ugyanaz az építő, amely egy sima URI- vagy JavaScript-kiváltót is lefed. A GoToE-nek nincs ilyen konstansa, és egyáltalán nincs útja abba az általános építőbe; az AddLinkToEmbeddedPDF az egyetlen mód, ahogyan a PDF Library for Delphi egyet konstruál, ami szigorúan oldal-hotspot akcióvá teszi, soha nem dokumentum- vagy oldal-szintű kiváltóvá. Ahol a GoToR és a Launch eléri az általános építőt, a kompromisszum a kontroll: felépít egy GoToR-t, amely csak egy nevezett távoli célhelyre mutat, és egy Launch-akciót csak egy fájlnévvel és paraméterekkel, míg az explicit oldal-és-illesztéstípus-címzés, és a Windows-specifikus indítási beállítások, amiket ez a cikk tárgyal, csak közvetlenül a SetActionRemoteDestinationEx-en és a SetActionLaunchOptions-on keresztül érhetők el

Egy biztonsági tulajdonságot érdemes ismerni, mielőtt egy karbantartási eszközt építesz e beállítók köré. A SetActionRemoteDestinationEx és a SetActionLaunchOptions előbb a teljes csereakciót építi fel egy piszkozat-szótárban, és csak akkor törli és másolja az /F, /D vagy /Win, és /NewWindow kulcsokat az élő akcióra, amikor az a piszkozat-másolat érvényesítésre kerül — így egy hívás, amely elbukja az érvényesítést, akár egy tartományon-kívüli ValueMask-ból, akár egy üres FileName-ből, teljesen érintetlenül hagyja az eredeti akciót, és bármely /Next láncot, amely már rajta lóg, ahelyett hogy félig felülírná. Ez azért számít, mert a GoToR- és Launch-akciók mindketten ülhetnek egy /Next láncon belül, amit az AddActionNextRemoteDestinationEx-szel, az AddActionNextLaunchEx-szel, vagy az általánosabb AddActionNextEx-szel építettek, lehetővé téve, hogy egyetlen kiváltó egy JavaScript-naplóbejegyzést, majd egy távoli ugrást váltson ki sorban. A GoToR-, GoToE-, és Launch-konstrukció, ahogy itt le van írva, a Delphihez és C++Builderhez készült natív PDF-könyvtár, a PDF Library for Delphi része