Teknik Makale

PDF Yaşam Döngüsü Eylemleri: Delphi'de Catalog /AA ile Page /AA

Yerel Delphi ve C++Builder PDF kütüphanesi olan PDF Library for Delphi, bir PDF belgesine otomatik davranışı asmak için iki ayrı yer verir: Catalog'un /AA sözlüğünde saklanan WillClose, WillSave, DidSave, WillPrint ve DidPrint gibi belge düzeyinde yaşam döngüsü eylemleri ve bunun yerine her Page nesnesinin kendi /AA sözlüğünde saklanan sayfa düzeyinde yaşam döngüsü eylemleri -Open ve Close. İki kapsayıcıyı karıştırmak, bir yaşam döngüsü eyleminin sessizce hiçbir şey yapmamasının tek en yaygın yoludur

Motive eden durumlar sıradandır. Bir finans ekibi, dosya yalnızca açıldığında değil, yazdırma gerçekten başladığı anda bir yazdırma zaman damgası basan ve kimin yazdırdığını günlüğe kaydeden bir hesap özeti şablonu ister. Formlar-yoğun bir iş akışı, okuyucunun PDF istemcisi pencereyi kapatmasına izin verilmeden önce alan değerlerinin otomatik olarak bir sunucuya itilmesine ihtiyaç duyar; böylece kapatılan bir sekme hiçbir zaman kayıp bir düzenleme anlamına gelmez. Çok sayfalı bir rapor, yalnızca o sayfa ekranda olduğu sürece görünen sayfaya özel bir banner ister. PDF gerçekte bu tür davranış için belge ve sayfanın altında üçüncü bir katman sunar -tek bir form alanının veya bağlantının kendi /A girdisine bağlı eylemler, etkileşimli form eylemleri ve JavaScript üzerine bir tamamlayıcı makalenin konusu- ama bu makale onun üzerindeki iki katmanda kalır: tüm belge ve tek bir sayfa

Hangi tetikleyiciler belge Catalog'unun /AA'sında yaşar?

Beş tetikleyici Catalog /AA sözlüğünde yaşar ve her biri tüm belgeyi etkileyen bir olay için tetiklenir, tek bir sayfayı değil. ISO 32000-1 §12.6.3 (Tetikleyici Olaylar), belge düzeyi anahtarlarını WC, WS, DS, WP ve DP olarak listeler -/AA sözlüğüne yazılan gerçek iki harfli adlar- sırasıyla WillClose, WillSave, DidSave, WillPrint ve DidPrint için ve PDF Library for Delphi bu kümeyi TPDFlibDocumentActionTrigger numaralandırmasında tam olarak yansıtır: datWillClose, datWillSave, datDidSave, datWillPrint, datDidPrint. SetDocumentAction, beşinden herhangi birini bağlayan tek giriş noktasıdır ve aldığı ActionKind parametresi, düz bir URI'den bir betiğe ve bir hedef atlamasına kadar, kütüphanedeki her eylem-kurucu çağrısında paylaşılan on PDF_ACTION_BUILDER_* sabitinden biridir. Bir GoTo, uzak-dosya, gömülü-dosya veya Launch eyleminin tetiklendiğinde gerçekte ne yaptığı, nereye bağlandığından farklı bir sorudur ve bu, GoTo, uzak, gömülü ve başlatma eylemleri üzerine bir tamamlayıcı makalenin konusudur -bu makale, eylem-türü sorusu yerine kapsayıcı sorusuyla, Catalog mı Page mi, kalır

PDF Library for Delphi şeması: tek bir PDF belgesi iki ek eylem kapsayıcısı sunar: beş belge yaşam döngüsü tetikleyicisini WC, WS, DS, WP ve DP tutan Catalog /AA ve yalnızca Aç ve Kapat sayfa tetikleyicilerini tutan her sayfanın /AA'sı
Catalog /AA girdisi beş belge yaşam döngüsü tetikleyicisini taşır; her Page /AA ise yalnızca Open ve Close çiftini bilir
var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.AddStandardFont(4);
    Lib.DrawText(40, 700, 'Quarterly statement');
    Lib.SetDocumentAction(datWillSave, PDF_ACTION_BUILDER_WEB,
      'https://example.com/audit/will-save', '', 0, 0);
    Lib.SetDocumentAction(datWillClose, PDF_ACTION_BUILDER_SUBMIT,
      'https://example.com/forms/submit', 'CustomerName;OrderTotal', 0, 0);
    Lib.SaveToFile('statement.pdf');
  finally
    Lib.Free;
  end;
end;

Bir sayfa düzeyi tetikleyici bir belge düzeyi olandan nasıl farklıdır?

Sayfa düzeyi bir tetikleyici yalnızca bağlı olduğu tek Page nesnesi için tetiklenir ve PDF Library for Delphi onu Catalog'unki yerine o sayfanın kendi /AA sözlüğünde saklar. Yalnızca iki sayfa tetikleyicisi vardır, Open ve Close, ISO 32000-1'in bir sayfanın ek-eylemler sözlüğü için tanımladığı O ve C anahtarlarına karşılık gelir ve PDF Library for Delphi bunları SetPageAction aracılığıyla patOpen ve patClose olarak sunar; bu, SelectPage ile o anda seçili olan sayfaya bağlanır -bir belge boyunca döngü yaparken bir çağrının her yere uygulanmasını beklediğiniz ilk seferde önemli bir ayrıntı, çünkü hiçbir zaman öyle olmaz. Her iki tür tetikleyiciyi bağlamak da dosyanın minimum PDF sürümünü yükseltir ve iki kapsayıcı farklı tabanlar ister: PDF Library for Delphi, bir Catalog /AA girdisi ilk yazıldığında belgeyi en az PDF 1.4'e ve bir Page /AA girdisi ilk yazıldığında en az PDF 1.5'e yükseltir, içinde hangi eylem türü oturuyor olursa olsun. Bu, eylemin kendisinin kendi başına gerektirdiğinin üzerine katmanlanmış kapsayıcı düzeyinde bir gerekliliktir; bu yüzden tek başına yalnızca PDF 1.1 gerektirecek çıplak bir URI eylemi, bir sayfa-açma tetikleyicisine sarıldığında yine de tüm dosyayı PDF 1.5'e çeker

Lib.SelectPage(3);
Lib.SetPageAction(patOpen, PDF_ACTION_BUILDER_JAVASCRIPT,
  'app.alert("Section 3: internal review only");', '', 0, 0);
Lib.SetPageAction(patClose, PDF_ACTION_BUILDER_WEB,
  'https://example.com/analytics/page-3-closed', '', 0, 0);

Yaşam döngüsü eylemlerini okumak ve kaldırmak

GetDocumentActionInfo ve GetPageActionInfo'nun ikisi de bir TPDFlibActionInfo kaydı döndürür ve o tetikleyiciye hiçbir şey bağlı olmadığında Kind alanı akNone olarak geri gelir; bu yüzden kayıttaki başka herhangi bir alana güvenmeden önce Kind'ı kontrol edin -URI, JavaScript, FileName ve geri kalanı, yalnızca Kind'ın gerçekte bildirdiği tek eylem türü için anlamlıdır, çünkü aynı kayıt şekli kurucunun üretebileceği her eylem türünde yeniden kullanılır. RemoveDocumentAction ve RemovePageAction'ın her biri tek bir tetikleyiciyi temizler ve kaldıracak bir şey bulduklarında 1, tetikleyici zaten boşken 0 bildirir; kaldırılan girdi /AA sözlüğünde kalan sonuncu olduğunda, PDF Library for Delphi, Catalog veya sayfada sarkan, anlamsız bir kapsayıcı bırakmak yerine artık boş olan /AA'yı kendisi siler

var
  Info: TPDFlibActionInfo;
begin
  Info := Lib.GetDocumentActionInfo(datWillSave);
  if Info.Kind = akURI then
    WriteLn('WillSave calls out to: ', string(Info.URI));

  if Lib.RemoveDocumentAction(datWillSave) = 1 then
    Lib.SetDocumentAction(datWillSave, PDF_ACTION_BUILDER_WEB,
      'https://example.com/audit/will-save-v2', '', 0, 0);
end;

PDF/A yaşam döngüsü eylemlerine hiç izin veriyor mu?

Hayır. PDF/A uyumluluğu, yalnızca riskli görünen eylem türlerini değil tüm ek-eylemler kapsayıcısını reddeder, çünkü ISO 19005, bir arşiv dosyasının o zamana kadar var olmayabilecek bir betik motoruna veya bir ağ bağlantısına bağımlı olmadan onlarca yıl sonra hâlâ aynı şekilde render edilmesi gerektiği varsayımıyla PDF'in etkileşimli-eylem modelini kısıtlar. Hem SetDocumentAction hem de SetPageAction'ın arkasındaki paylaşılan kurucu olan SetLifecycleAction, ActionKind'a hiç bakmadan önce PDFAMode'u kontrol eder; bu yüzden yalnızca bir şirket web sayfasını açan bir URI eylemi veya yalnızca bir sonraki sayfaya git anlamına gelen bir Named eylemi, tehlikeli olanla aynı ağa yakalanır -bir güvenlik incelemecisinin normalde işaretlemeyeceği hiçbir şey, yine de engellenir, çünkü kısıtlama duruma göre değil yapısaldır. Pratik tehlike, reddin sessiz olmasıdır: SetDocumentAction ve SetPageAction'ın ikisi de bir istisna fırlatmadan 0 döndürür; bu yüzden dönüş değerini hiç kontrol etmeyen bir çağrı yeri, taşıması gereken tetikleyiciyi sessizce eksik bırakan bir belge gönderir

PDF Library for Delphi: PDF/A uyumluluk kapısının akış şeması: SetDocumentAction ve SetPageAction, önce PDFAMode'u kontrol eden, /AA girişini yazan ve PDF/A modu yokken 1 döndüren ancak PDF/A modunda sessizce 0 döndüren SetLifecycleAction'dan geçer; kaldırma çağrıları ise kapıyı tamamen atlar
PDF/A altında paylaşılan oluşturucu, her yaşam döngüsü eylemini sessiz bir sıfır dönüşüyle reddeder; tetikleyicileri kaldırmak ise izinli kalır
Lib.SetPDFAMode(2); // PDF/A-1b
if Lib.SetDocumentAction(datWillClose, PDF_ACTION_BUILDER_NAMED,
     '', '', 0, 0) = 0 then
  // reddedildi: PDF/A-1b, düz bir Named eylemi olsa bile Catalog /AA'ya izin vermez
  WriteLn('lifecycle action not attached');

Akılda tutulmaya değer bir asimetri var. RemoveDocumentAction ve RemovePageAction, PDFAMode'u hiç kontrol etmez; bu yüzden zaten uyumsuz yaşam döngüsü eylemleri taşıyan bir dosyayı yükleyip PDF/A-uyumlu bir kayda giderken onları çıkarmak beklendiği gibi çalışır -yalnızca yazma yolu, yeni bir tetikleyici bağlamak, uyumluluk moduna kapılanmıştır

Bir WillOpen tetikleyicisi olmadan açılışta-yazdırma nereye oturur?

Catalog /AA sözlüğünün hiç WillOpen girdisi yoktur, tasarım gereği -ISO 32000-1'de belge düzeyi /AA, tam olarak beş anahtar tanımlar, WillClose, WillSave, DidSave, WillPrint ve DidPrint ve o listedeki hiçbir şey yalnızca bir dosya açıldığı için tetiklenmez. Açılış-zamanı kancası, PDF Library for Delphi'ın kendi çağrı ailesi aracılığıyla sunduğu ayrı bir Catalog girdisinde, /OpenAction'da yaşar; bunların arasında SetOpenActionJavaScript, SetOpenActionDestination ve SetOpenActionNamedDestination vardır ve hiçbiri /AA sözlüğüne veya TPDFlibDocumentActionTrigger numaralandırmasına hiç dokunmaz. Ancak iki mekanizma birleşir ve bir açılışta-yazdırma şablonunun gerçekte genellikle ihtiyaç duyduğu şey budur: şablonu, /OpenAction'ının yazdırma işini başlatacağı şekilde kurun, tipik olarak görüntüleyicinin kendi yazdırma komutunu çağıran bir JavaScript eylemi ve yazdırmanın kendisi, WillPrint ve DidPrint'e karşı çalışacak bir şey verir -sayfalar spool'a girmeden önce basılmış bir zaman damgası, bittiklerinde yazılan bir denetim girdisi

PDF Library for Delphi: Catalog /AA'da WillOpen anahtarının bulunmadığını gösteren bileşim şeması: görüntüleyicinin açma olayı bir /OpenAction JavaScript yazdırma işi başlatır; bu, sayfalar kuyruğa alınmadan önce WillPrint'in zaman damgası basmak için, ardından DidPrint'in denetim girişini yazmak için tetiklenmesini sağlar
Açılışta yazdırma, Catalog /OpenAction yazdırma işini, kuyruğa almadan önce WillPrint damgalaması ve ardından DidPrint günlüklemesiyle birleştirir

Bu tetikleyiciler PDF görüntüleyicileri genelinde ne kadar güvenilir?

PDF/A dışında bile her görüntüleyici bunları çalıştırmaz; bu yüzden bir yaşam döngüsü eylemini bir garanti değil bir istek olarak ele alın. Acrobat ve çoğu tam masaüstü okuyucusu tüm kümeyi sadakatle çalıştırır, ama gerçek dünya PDF tüketiminin büyük bir kısmı bir ek-eylemler sözlüğüne hiç dokunmaz: tarayıcıya-gömülü görüntüleyiciler, çoğu mobil okuyucu ve neredeyse her sunucu tarafı render veya metin çıkarma boru hattı ya /AA'yı tamamen göz ardı eder ya da yalnızca onun dar bir dilimini onurlandırır; başsız dönüşümün onlara bağlanacak bir yazdırma işlemi olmadığından WillPrint ve DidPrint tipik olarak en kötü performansı gösterir. Bir WillClose gönderim-formu eylemi form verisini yakalayan tek yolsa, güvenilir bir yol değildir -onu açık bir gönder düğmesiyle eşleştirin ve otomatik tetikleyiciyi onu destekleyen okuyucular için bir kolaylık olarak ele alın

Belge, sayfa ve alan tetikleyicileri, aynı altta yatan eylem-sözlüğü makinesinin üç katmanıdır ve kapsayıcı netleştiğinde, geri kalanı doğru ActionKind sabitini seçmek ve dönüş kodunu kontrol etmektir. Bu yaşam döngüsü tetikleyicileri, bu makalenin değindiği daha geniş eylem-kurucu API'siyle birlikte, standart PDF Library for Delphi Delphi PDF kütüphanesi'nin bir parçası olarak, ürün belgelerinde tam tetikleyici ve eylem-türü referansıyla birlikte gönderilir