Techninis straipsnis

GoToR, GoToE ir Launch veiksmai Delphi PDF failuose

PDFlibPas Delphi ir C++Builder kūrėjams suteikia tris veiksmų rūšis navigacijai, kuri palieka dabartinį puslapį: GoToR (Go To Remote) atveria konkretų puslapį kitame PDF faile, GoToE (Go To Embedded) atveria dabartiniame dokumente įterptą PDF failą, o Launch paleidžia išorinę programą arba atveria failą per operacinės sistemos apvalkalą. Visos trys rūšys aprašytos ISO 32000-1 §12.6.4 veiksmų tipų skyriuje, kuriame taip pat apibrėžtas įprastas GoTo veiksmas, ir kiekviena turi savų spąstų: puslapio numerį, kurio reikšmė priklauso nuo jį sukuriančio iškvietimo, taikinį, kuris yra vardas, o ne failo kelias, ir dvi eilutes, kurios atrodo vienodos, bet skirtingose peržiūros programose atlieka du skirtingus vaidmenis

Visa tai nėra hipotetika. Techninės informacijos paketas — pagrindinis vadovas, specifikacijų PDF, kurį platintojas atnaujina pagal savo grafiką, ir kartu su abiem įdiegtas kalibravimo įrankis — remiasi būtent tokiu kelių dokumentų susiejimu: kryžmine nuoroda, kuri turi nukreipti į specifikacijų failo 5 puslapį, duomenų lapu, kurį verta laikyti vadovo viduje, o ne šalia jo, ir nuoroda, tiesiogiai perduodančia valdymą kalibravimo įrankiui. Šis straipsnis yra priešinga žymelių ir anotacijų veiksmų nuskaitymo iš esamo PDF pusė: anas tekstas aprašo GoToR, Launch arba GoToE veiksmų, kuriuos į failą jau įrašė kitas kūrėjas, skaitymą, o šis — tų pačių trijų veiksmų rūšių kūrimą nuo nulio, įskaitant laukų taisykles, kurias PDFlibPas taiko prieš įrašydamas nors vieną baitą

Trys būdai, kuriais PDF veiksmas palieka dabartinį puslapį

PDFlibPas atskiria vietinę navigaciją nuo visų kitų veiksmų pagal veiksmo raktą /S, o GoToR, GoToE ir Launch yra trys potipiai, kurių taikinys yra už dabartinio puslapio ribų: GoToR aprašytas ISO 32000-1 §12.6.4.3, GoToE — §12.6.4.4, o Launch — §12.6.4.5, visi platesniame §12.6.4 veiksmų tipų skyriuje, kuriame taip pat apibrėžtas įprastas GoTo veiksmas. Paprasto GoTo veiksmo paskirties vieta nurodo dokumente jau esantį puslapio objektą, todėl PDFlibPas gali jį iškart patikrinti; GoToR ir GoToE taip pat patikrinti negali, nes išorinis failas šioje kompiuterio sistemoje gali neegzistuoti, o įterpto failo puslapių skaičiaus pagrindinis dokumentas neseka, todėl abu naudoja neišspręstą nuorodą vietoje tiesioginės nuorodos — GoToR naudoja failo specifikaciją ir paskirties vietą, GoToE — įterpto failo vardą ir taikinio puslapį — o Launch visiškai atsisako paskirties vietos sąvokos ir tiesiog nurodo tai, ką turi paleisti arba atverti operacinė sistema. Rašymo pusėje šis skirtumas pasireiškia dviem iškvietimų grupėmis: aukšto lygio vieno veiksmo kūrėjai, tokie kaip AddLinkToFile, AddLinkToFileEx, AddLinkToEmbeddedPDF ir AddLinkToLocalFile, kartu sukuria puslapio taško nuorodos anotaciją ir jos veiksmą, todėl tinka daugumai realių maketų — teksto eilutei arba piktogramai, kurią skaitytojas spusteli — o žemesnio lygio nustatymo metodai, tokie kaip SetActionRemoteDestinationEx, SetActionLaunchOptions ir jų AddActionNext* atitikmenys, prideda arba pakeičia veiksmą objekte, kurio rankeną jau turite: esamoje žymelėje, formos lauko įvykio apraše arba dokumento ar puslapio gyvavimo ciklo įvykyje. Abi grupės galiausiai įrašo tos pačios formos žodynus; skiriasi tik tai, kur esate iškvietimo metu ir, kaip aptariama kitame skyriuje, ką reiškia puslapio numeris

Kaip sukurti GoToR nuorodą, atveriančią puslapį kitame PDF faile

GoToR veiksmui reikia dviejų dalykų — failo specifikacijos ir paskirties vietos tame faile — o PDFlibPas siūlo du skirtingus iškvietimus antrajai daliai pateikti, kiekvienas su savo puslapių numeravimo taisykle. AddLinkToFile ir AddLinkToFileEx, aukšto lygio puslapio taško nuorodos kūrėjai, tikrina, ar jų Page arba DestPage argumentas yra didesnis už nulį, tai yra tas pats numeravimas nuo 1, kurį PDFlibPas naudoja visur kitur, įskaitant SelectPage. Žemesnio lygio nustatymo metodas SetActionRemoteDestinationEx, naudojamas GoToR veiksmui pridėti arba pakeisti objekte, kurio rankeną jau turite, vietoj to tikrina, ar DestPage yra didesnis arba lygus nuliui, ir įrašo jį tiesiai į aiškios paskirties masyvą be jokio koregavimo: jis tikisi taikinio dokumento neapdoroto, nuo nulio prasidedančio puslapio indekso, kurį ISO 32000-1 nurodo nuotolinei aiškiai paskirties vietai. Jei žemesnio lygio metodui perduosite tą patį skaičių, kurį perduotumėte aukšto lygio kūrėjui, nuoroda atvers vienu puslapiu anksčiau

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;

Kiti SetActionRemoteDestinationEx argumentai taip pat yra tiesioginės reikšmės. ValueMask yra bitų rinkinys — 1 kairiajai, 2 viršutinei, 4 dešiniajai, 8 apatinei kraštinei ir 16 masteliui — o PDFlibPas prieš ką nors įrašydamas tikrina jį pagal DestType: dkFitR paskirties vietai reikia tiksliai 15 (visos keturios kraštinės be mastelio), dkFit ir dkFitB turi būti 0, o dkFitH/dkFitV priima tik vieną atitinkamą koordinatę. Nepasirinkti bitai galiojančioje kaukėje iš masyvo nepašalinami — jie įrašomi kaip aiški PDF nulinė reikšmė, kurią ISO 32000-1 aiškina kaip „palikti bet kokią peržiūros programoje jau esančią reikšmę“ tai koordinatei — tai teisėtas būdas pasakyti „pereiti į šį puslapį, bet nekeisti mastelio“, o ne pamiršimas. Pats mastelis saugomas kaip perduotos reikšmės trupmena, todėl iškvietus 150 procentų masyve saugoma reikšmė 1.5, o galiojantis įvesties intervalas yra nuo 0 iki 6400

Kaip susieti su PDF failu, įterptu jūsų dokumente

AddLinkToEmbeddedPDF sukuria GoToE veiksmą, o jo taikinio argumentas EmbeddedFileName yra vardas, ne kelias: jis turi sutapti su eilute Title, anksčiau perduota EmbedFile, kai buvo kuriamas priedas, nes šis pavadinimas yra pažodinis raktas, kurį PDFlibPas įrašo į dokumento /EmbeddedFiles vardų medį, o GoToE ieško būtent to vardo, dar kartą neliesdamas failų sistemos. Funkcija tikrina tik tai, ar EmbeddedFileName nėra tuščias ir ar TargetPage yra bent 1 — jei perduosite vardą, kuris niekada nebuvo įterptas, iškvietimas vis tiek grąžins sėkmę, veiksmas vis tiek bus įrašytas, o spustelėjusių skaitytojų peržiūros programos tiesiog negalės rasti taikinio

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;

Čia susideda ne viena, o dvi versijos ribos. EmbedFile vardų medžiui /EmbeddedFiles reikia PDF 1.4, o AddLinkToEmbeddedPDF atskirai pakelia ribą iki PDF 1.6 dėl paties GoToE veiksmų tipo, todėl dokumentui, kuriame naudojama ši funkcija, veiksminga mažiausia versija yra 1.6, o ne 1.4. Taip pat atkreipkite dėmesį, kad TargetPage čia numeruojamas nuo 1, pagal įprastą PDFlibPas konvenciją — tai sąmoningas kontrastas su ankstesniame skyriuje aptartu nuo nulio pradedamu DestPage ir priminimas, kad taikoma puslapių numeravimo schema priklauso nuo veiksmo rūšies ir konkretaus iškvietimo, o ne nuo vienos bendros taisyklės. Veiksmo taikinio žodyne taip pat gali būti įrašas /R su reikšme C, reiškiančia antrinį, arba P, reiškiančia pirminį taikinį, todėl galima sukurti dviejų šuolių grandinę į įterptą failą arba atgal į jį talpinantį dokumentą, nors AddLinkToEmbeddedPDF visada kuria tik antrinę kryptį, nes ji prasminga dokumentui, kuris įterpia failą, o ne pačiam įterptam failui

Launch veiksmai: vienas FileName, du nesuderinami eilučių taikiniai

SetActionLaunchOptions įrašo Launch veiksmo failo taikinį į du skirtingus raktus, naudodamas vieną FileName argumentą, o tuose raktuose laikomos dviejų skirtingų rūšių eilutės. Aukščiausio lygio raktas /F gauna failo specifikacijos žodyną, sukurtą naudojant tą patį kelio konvertavimą, kurį PDFlibPas naudoja GoToR veiksmui, ir tai yra nešiojamoji forma, kurią ISO 32000-1 §7.11.3 apibrėžia failo specifikacijos žodynui. /Win antrinis žodynas, kai PDFlibPas jį įrašo, gauna savo raktą /F, kurio reikšmė yra neapdorota FileName reikšmė tiksliai tokia, kokia perduota, be jokio konvertavimo, nes /Win /F ISO 32000-1 §12.6.4.5 aprašytas kaip paprasta Windows kelio eilutė, skirta tik Windows peržiūros programai. Jei, tikėdamiesi, kad abu raktai bus vienodi, perduosite nešiojamąjį, jau konvertuotą kelią, /Win kopijoje liks tai, ką perdavėte funkcijai, nepakeista

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 laikykite daugiausia trinties sukeliančiu iš trijų veiksmų, nes jo vienintelė paskirtis — paleisti programą arba atverti failą už PDF smėlio dėžės ribų, todėl kiekviena pagrindinė peržiūros programa į jį reaguoja atsargiai. „Adobe Acrobat“ patobulinta sauga pagal numatytuosius nustatymus blokuoja Launch veiksmus arba rodo įspėjimą, nebent taikinys yra aiškiai patikimoje vietoje, o dauguma įmonių „Acrobat“ diegimų šią apsaugą palieka įjungtą. Todėl viešai platinamame dokumente Launch veiksmas nėra patikimas paleidiklis: numatykite, kad bet kuri dokumentą atverianti peržiūros programa jį užblokuos, paprašys patvirtinimo arba tyliai ignoruos, ir naudokite jį uždarose aplinkose, kuriose taip pat valdote peržiūros programos patikimumo nustatymus — vidiniame kioske, kontroliuojamame įmonės diegime ar dokumente, kuris niekada nepalieka jūsų administruojamo kompiuterio

PDF/A vartai: kodėl GoToR ir Launch iškvietimai gali grąžinti nulį

SetActionRemoteDestinationEx ir SetActionLaunchOptions iškart atsisako veikti, kai taikinio dokumentui nustatytas bet koks PDF/A atitikties režimas: abu pirmiausia patikrina dokumento PDF/A režimą ir, prieš paliesdami veiksmą, išeina su rezultatu 0, nesukeldami išimties. Tai tyčinis elgesys. PDF/A interaktyvių veiksmų apribojimai konkrečiai neleidžia Launch veiksmui, nes archyvinio failo galimybė paleisti savavališką programą yra būtent nuo aplinkos priklausantis elgesys, kurio ilgalaikio archyvavimo formatai siekia išvengti, o PDFlibPas tą patį konservatyvų vartus taiko ir nuotolinio perėjimo nustatymo metodui tame pačiame kodo kelyje. Kuriant lengva nepastebėti praktinės pasekmės: identiškas iškvietimas, kuris veikia įprastame PDF, dokumente su nustatytu PDF/A atitikties lygiu sukompiliuos, paleis programą ir tyliai nieko neatliks, todėl tikrinkite grąžinamą reikšmę, o ne manykite, kad operacija pavyko — 0 čia nereiškia netinkamų įvesties duomenų, tai bibliotekos atsisakymas vykdyti užklausą, prieštaraujančią paties dokumento atitikties deklaracijai

Kur GoToR, GoToE ir Launch telpa platesnėje PDFlibPas darbo eigoje

Visos trys šiame straipsnyje aprašytos veiksmų rūšys pasiekia ne tas pačias vietas. Papildomame straipsnyje apie dokumento ir puslapio gyvavimo ciklo veiksmų paleidiklius aprašyti SetDocumentAction ir SetPageAction, kurie gali prijungti GoToR arba Launch veiksmą prie tokio paleidiklio kaip WillClose, naudodami bendrus PDF_ACTION_BUILDER_REMOTE_DESTINATION ir PDF_ACTION_BUILDER_LAUNCH konstantų vardus — tas pats kūrėjas taip pat apima paprastą URI arba JavaScript paleidiklį. GoToE neturi tokios konstantos ir apskritai neturi kelio į šį bendrą kūrėją; AddLinkToEmbeddedPDF yra vienintelis būdas, kuriuo PDFlibPas sukuria GoToE, todėl jis yra griežtai puslapio taško veiksmas, niekada dokumento ar puslapio lygio paleidiklis. Kai GoToR ir Launch pasiekia bendrą kūrėją, tenka atsisakyti dalies valdymo: jis sukuria GoToR, nukreipiantį tik į pavadintą nuotolinę paskirties vietą, ir Launch veiksmą, turintį tik failo vardą bei parametrus, o šiame straipsnyje aprašytas aiškus puslapio ir pritaikymo tipo adresavimas bei Windows būdingi paleidimo nustatymai pasiekiami tik tiesiogiai per SetActionRemoteDestinationEx ir SetActionLaunchOptions

Prieš kuriant priežiūros įrankį, paremtą šiais nustatymo metodais, verta žinoti vieną saugumo savybę. SetActionRemoteDestinationEx ir SetActionLaunchOptions pirmiausia sukuria visą keičiamo veiksmo kopiją laikinojo žodyno srityje, o raktus /F, /D arba /Win bei /NewWindow į gyvą veiksmą ištrina ir nukopijuoja tik tada, kai laikina kopija patikrinama, todėl patikros neatlaikęs iškvietimas, nesvarbu, ar jį sukėlė už leistino intervalo esantis ValueMask, ar tuščias FileName, palieka pradinį veiksmą ir visą prie jo jau prijungtą /Next grandinę visiškai nepakeistus, o ne pusiau perrašytus. Tai svarbu, nes GoToR ir Launch veiksmai gali būti /Next grandinėje, sukurtoje naudojant AddActionNextRemoteDestinationEx, AddActionNextLaunchEx arba bendresnį AddActionNextEx, todėl vienas paleidiklis gali nuosekliai įrašyti JavaScript žurnalo įrašą, o tada atlikti nuotolinį perėjimą. Čia aprašytas GoToR, GoToE ir Launch kūrimas yra PDFlibPas dalis — tai gimtoji PDF biblioteka, skirta Delphi ir C++Builder