Evet — PDFium, önemli bir ayrım dışında XFA formlarını destekler. PDFium Bileşeni, standart DLL kullanarak sıfır çalışma zamanı bağımlılığı ile bir XFA formunu algılayabilir ve template, datasets ve config paketlerini ham XML olarak çıkarabilir. Dinamik bir XFA formunu çalıştırmak — XML açıklamasını canlı, etkileşimli sayfalara yerleştirmek — ek olarak V8 özellikli derleme gerektiren ayrı bir sorundur. Topluluk, PDFium'un "XFA yapıp yapmadığını" sanki bu evet ya da hayır denebilecek tek bir yetenekmiş gibi sormaya devam ediyor; dürüst cevap ise XFA verilerini okumak ile bir XFA formunu işlemenin (render) iki çok farklı bağımlılık ayak izine sahip çok farklı iki şey olduğudur
Bu makale, Delphi ve C++Builder için PDFium'un yerel VCL sarmalayıcısı olan PDFium Bileşeni ile her iki yarıyı da inceler. Önce algılama ve statik çıkarma yolu — FormType, XFA özelliği ve GetXfaPacket* okuyucuları — ardından V8 kısıtlamasıyla dinamik çalışma zamanı yolu ve son olarak dağıtılan bir DLL motoru çalıştıramadığında zarif bir şekilde başarısız olmanızı sağlayan yetenek kontrolleri
PDFium XFA formlarını destekliyor mu?
Bunu tek bir yetenek olarak değil, iki yetenek olarak okuyun. Statik XFA çıkarma her zaman mevcuttur: GetXfaPacketCount, GetXfaPacketName og GetXfaPacketContent özelliklerini destekleyen düşük seviyeli FPDF_GetXFAPacket* dışa aktarımları, XFA devre dışı bırakılmış bir derleme de dahil olmak üzere makul derecede modern her pdfium.dll dosyasına derlenmiştir. Bu nedenle bir formdan ham template, datasets og config XML'lerini çekmek çalışma zamanı bağımlılığı taşımaz — bu işlem saf yapısal okumadır, motor dahil olmaz. Dinamik XFA ise diğer yetenektir: formu çalıştırmak, hesaplamalarını değerlendirmek ve XFA düzen motorunun sayfalandırmasına izin vermek. Bu yol yalnızca V8 JavaScript motorunun üzerinde XFA desteğiyle derlenmiş bir PDFium sürümünde mevcuttur ve çoğu "PDFium XFA destekliyor mu" tartışmasının gerçekten tartıştığı kısımdır
Standartlar ayrımı net bir şekilde çerçeveler. XFA, Adobe XFA 3.3 (2012) içindeki temel PDF dilbilgisinin dışında tanımlanmıştır ve PDF 1.7 §8.6.7, bir XFA formunun bir PDF içinde nasıl taşındığını açıklar: form paketleri AcroForm sözlüğü /XFA girdisinin altında yaşar ve bir XFA işlemcisi tarafından düzenlenmesi gereken bir belge katalogda /NeedsRendering true ayarlar. Bu iki işaretçi, PDFium Bileşeni'nin tam olarak aradığı ve daha önce hiç görmediğiniz bir dosya hakkında mantık yürütürken güvenebileceğiniz şeylerin aynısıdır
XFA'nın AcroForm'dan farkı nedir?
Bir AcroForm klasik PDF formudur: widget açıklamaları — metin alanları, onay kutuları, radyo grupları — sıradan PDF sayfalarında sabit koordinatlara sabitlenmiştir. Gördüğünüz sayfa dosyadaki sayfadır ve alanlar bunun üzerinde yer alır. XFA ise tam tersi bir yaklaşım sergiler. Etkileşimli form tamamen XML ile tanımlanır ve tam (dinamik) bir XFA belgesinde PDF sayfaları aslında bir yer tutucudur — gerçek içerik, açılış zamanında XML şablonunu okuyan ve veriye sığacak şekilde büyüyen veya küçülen bir XFA motoru tarafından üretilir. /NeedsRendering true ifadesinin duyurduğu şey budur: bir XFA işlemcisi olmadan, statik sayfalar boş olabilir veya yalnızca "lütfen uyumlu bir görüntüleyicide açın" uyarısı taşıyabilir
XFA veri yükünün kendisi bir dizi adlandırılmış pakettir ve bunlardan üçü çoğu entegrasyonun ihtiyaç duyduğu her şeyi taşır. template paketi form tanımıdır — alanlar, düzen, hesaplamalar ve komut dosyaları. datasets paketi gerçek örnek verilerini, doldurulmuş alan değerlerini bir XML ağacı olarak tutar. config paketi ise XFA motoru için işleme talimatlarını taşır. Diğerleri de mevcuttur — localeSet, connectionSet, xdp sarmalayıcı — ancak bir formu denetlemek veya verileri dışarı aktarmak için şablon artı veri kümeleri genellikle işin tamamıdır. Bu veri sadece bir akışta duran XML olduğundan, onu okumak asla bir şey yürütmeyi gerektirmez; bu da statik çıkarmanın bağımlılıksız olmasının temel nedenidir
Delphi'de bir XFA formunu algılama
Algılama, her PDFium Bileşeni görevinin başladığı şekilde başlar: FileName ayarlayın, Active := True yapın ve gerçekten açıldığını kontrol edin. Active := True asla hata oluşturmaz — eksik bir dosya, yanlış şifre veya olmayan DLL sadece Active durumunu False olarak bırakır — bu nedenle koruma zorunludur. Belge açıldıktan sonra, FormType size hangi form modelini kullandığını söyler. İki TPdfFormType değeri XFA'dır: ftXfaFull, işlenmesi gereken tam, dinamik bir XFA formudur; ftXfaForeground ise XFAF'tir; yani XFA içeriğinin normal AcroForm sayfaları üzerine çizildiği ve statik sayfaların anlamlı kalmaya devam ettiği ön plan alt kümesidir. XFA boolean değeri, "bu her iki türden de bir XFA belgesidir" ifadesinin bir kısayoludur. XFAF veya AcroForm belgesi üzerindeki widget düzeyindeki alanlarla da çalışıyorsanız, işleyiş PDFium form alanı navigasyonu kılavuzunda ele alınmıştır
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'application-form.pdf';
Pdf.Active := True;
if not Pdf.Active then
Exit; // damaged, encrypted, or DLL missing
case Pdf.FormType of
ftXfaFull:
Log('Full (dynamic) XFA form');
ftXfaForeground:
Log('XFAF: XFA layered over static AcroForm pages');
ftAcroForm:
Log('Classic AcroForm');
else
Log('No interactive form');
end;
if Pdf.XFA then
Log('Document carries an XFA packet payload');
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
XFA paketlerini ham XML olarak çıkarma
Çıkarma, yüksek değerli, sıfır bağımlılıklı yarıdır. Yüklenen DLL'de paket okuyucu dışa aktarımlarının çözüldüğünü onaylayan XfaStaticReadable ile koruyun, ardından paketleri dizine göre tarayın. GetXfaPacketCount formun kaç paket taşıdığını döndürür, GetXfaPacketName her birine adını verir ve GetXfaPacketContent ham baytları döndürür, bunlar bilinen paketler için UTF-8 XML'dir. Hangi paketi istediğinizi zaten bildiğinizde, adlandırılmış yardımcılar GetXfaTemplate, GetXfaDatasets ve GetXfaConfig sizin için adı çözümler ve doğrudan TBytes teslim eder. Bir veri taşıma işi için önemli olan GetXfaDatasets'tir: size gönderilen alan değerlerini, bir PDF'den metin çıkardıktan sonra yapacağınız benzer bir iş akışı çalışması gibi ayrıştırıp kendi şemanıza eşleyebileceğiniz bir XML belgesi olarak verir
procedure ExtractXfaData(Pdf: TPdf; const Dir: string);
var
I, Count: Integer;
PacketName: string;
Content, Datasets: TBytes;
begin
if not Pdf.XfaStaticReadable then
Exit; // packet reader exports not present
Count := Pdf.GetXfaPacketCount;
for I := 0 to Count - 1 do
begin
PacketName := Pdf.GetXfaPacketName(I);
Content := Pdf.GetXfaPacketContent(I); // raw UTF-8 XML
TFile.WriteAllBytes(
TPath.Combine(Dir, PacketName + '.xml'), Content);
end;
// Or jump straight to the filled-in field values by name:
Datasets := Pdf.GetXfaDatasets;
if Length(Datasets) > 0 then
ParseAndMap(Datasets);
end;
Dinamik XFA işleme neden V8 motoruna ihtiyaç duyar?
Öncelikle sonuç: çünkü dinamik bir XFA formu statik bir çizim değil, küçük bir programdır. Şablon paketi FormCalc ve JavaScript hesaplamalarını, doğrulamalarını ve olay komut dosyalarını taşır ve XFA düzen motorunun sayfaların neye benzeyeceğine karar vermek için bunları yürütmesi gerekir. PDFium bu motoru yalnızca Google'ın JavaScript motoru olan V8'in üzerinde XFA desteğiyle derlenmiş bir sürümde uygular; V8 özellikli DLL dosyasının (pdfium32v8.dll veya pdfium64v8.dll) standart sürümden belirgin şekilde daha büyük olmasının nedeni de budur. V8 olmadan komut dosyalarını çalıştıracak kimse yoktur, bu nedenle gösterilecek işlenmiş sayfalar da yoktur, yalnızca statik olarak okuyabileceğiniz ham XML vardır
Bu derlemeyi seçmenin etrafından dolaşamayacağınız sert bir kısıtlaması vardır: bu, işlem başına bir kez yapılan bir karardır. Tek bir işlem hem standart pdfium.dll dosyasını hem de V8 özellikli derlemeyi yükleyemez — aynı sembolleri dışa aktarırlar ve V8 küresel yalıtılmış alan durumunu korur — bu nedenle PDFium Bileşeni, kütüphane ilk kez yüklenmeden önce V8 derlemesini seçmelidir. Bunu otomatik hale getirmek için, LoadDocument dosyayı /XFA ve /NeedsRendering true işaretçileri için önceden tarar (bağımsız PdfFileLooksXfa işleviyle aynı kontrol) ve bunları bulduğunda yüklemeden önce EnableV8Engine := True olarak ayarlar. Düz bir pdfium.dll zaten yüklendiğinde, geçiş artık yürürlüğe giremez ve çalışma zamanı eksik yolu bu durumu bildirir
begin
// Peek for XFA before PDFium is loaded for the first time, so the
// V8-enabled build can still be selected. PdfFileLooksXfa scans for
// /XFA and /NeedsRendering true without parsing the whole file.
if PdfFileLooksXfa('dynamic-form.pdf') then
EnableV8Engine := True;
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'dynamic-form.pdf';
Pdf.Active := True;
if Pdf.Active and Pdf.XFA then
begin
if Pdf.XfaRuntimeAvailable then
RenderXfaPages(Pdf) // engine is live: dynamic layout
else
ExtractXfaData(Pdf, 'C:\out'); // fall back to static packets
end;
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
Eksik bir XFA çalışma zamanını zarif bir şekilde yönetme
Üç kontrol size tam olarak ne kadar XFA yeteneğine sahip olduğunuzu söyler ve dürüst kod varsayımda bulunmak yerine bunları kontrol eder. XfaFeaturesAvailable, yüklenen DLL'de XFA/V8 yardımcı dışa aktarımlarının mevcut olduğuna dair küresel bir kontroldür. XfaStaticReadable, paket okuyucuların çözüldüğünü belge bazında onaylar — çıkarma için koruma. XfaRuntimeAvailable ise güçlü olanıdır: yalnızca bu özel belge XFA motorunu gerçekten etkinleştirdiğinde, yani XFA olduğunda, V8 derlemesi yüklendiğinde ve FPDF_LoadXFA başarılı olduğunda true olur. Dağıtılan DLL'de XFA eksik olduğunda veya standart derleme zaten yüklendiğinde bir belge XFA = True ancak XfaRuntimeAvailable = False rapor edebilir
Bir XFA belgesi açıldığında ancak motor çalışamadığında, PDFium Bileşeni kumar oynamaz. Formu, onurlandıramayacağı dinamik bir çalışma zamanı istemek yerine güvenli statik moda sabitler ve ana bilgisayarın tepki verebilmesi için OnXfaRuntimeMissing olayını bir kez tetikler — bu genellikle kullanıcıya V8 özellikli derlemeyle yeniden başlatmasını söylerken, bu sırada statik olarak okunabilir verileri sunmaya devam etmektir. Bu işleyiciyi bağlamak, temiz bir geri çekilme ile sessizce hiçbir şey göstermeyen bir form arasındaki farktır
procedure TForm1.PdfXfaRuntimeMissing(Sender: TObject);
begin
// Fires once when an XFA document opens but the engine cannot run:
// a plain pdfium.dll was already loaded, or the build lacks XFA/V8.
ShowMessage('This is a dynamic XFA form. Restart with the ' +
'V8-enabled build to render it; its packet data is still readable.');
end;
Kendi ürününüzdeki sınır konusunda da dürüst olun. V8 özellikli DLL'i asla gönderemezseniz, dinamik XFA formları kullanıcılarınız için asla işlenmeyecektir — ancak bunları algılayabilir, her paketi çıkarabilir ve gönderilen verileri dışarı çekebilirsiniz; bu da bunları etkileşimli olarak sunmak yerine eski devlet ve banka formlarından veri çekmekle ilgili olan büyük iş sınıfı için yeterlidir. Daha ağır olan V8 derlemesini gerçekten canlı, doldurulabilir bir forma ihtiyaç duyan durumlar için saklayın ve yetenek kontrollerinin her belgeyi gerçekten gidebileceği yola yönlendirmesine izin verin. Çıkarma hattınız gömülü dosyaları da yönetiyorsa, aynı salt okunur felsefe Delphi'deki PDF ekleri için de geçerlidir
Burada gösterilen FormType, XFA, GetXfaPacketCount, GetXfaPacketName, GetXfaPacketContent, GetXfaTemplate, GetXfaDatasets, GetXfaConfig ve yetenek kontrolü API'leri Delphi ve C++Builder için PDFium Bileşeni'nin bir parçasıdır