PDFium Bileşeni'ndeki TPdf.SetFocusedFormFieldText, o anda odaklanılan form alanının canlı düzenleme arabelleğine yazar ve bir XFA formu için o arabellek diske serileştirilen datasets paketine hiçbir zaman ulaşmaz -bu yüzden bir kullanıcının yazdığı ve kodunuzun kabul edildiğini doğruladığı bir değer, dosya bir sonraki açıldığında sessizce yok olmuştur. AcroForm alanlarının bu sorunu yoktur: aynı çağrı, odak uzaklaştığı anda alanın /V girdisine işlenir. Bir XFA giriş formunu dolduran, kaydeden ve tutar alanının yeniden boş olduğunu görmek için yeniden açan bir kullanıcı bir render hatasına çarpmıyordur -PDFium motorunun kendisinin form verisi yazmak için sunduğu şeyin sınırına çarpıyordur
Bu, bir XFA formunu en baştan tespit etmekten veya JavaScript'inin çalışmasını sağlamaktan daha dar bir sorudur: "PDFium XFA'yı destekliyor mu" değil ve "AcroForm betiklerini nasıl çalıştırırım" değil, özellikle SetFocusedFormFieldText başarı bildirdikten sonra bir değere ne olduğu. Kısa versiyon şu: AcroForm ve XFA, PDFium'un yazma yolu açısından aynı form modelinin iki lehçesi değildir -bir kullanıcının yazdığı ile bir kaydın gerçekte yakaladığı şey arasında tamamen farklı iki ilişkiye sahip iki form modelidir ve ikisini karıştırmak, tek satırlık bir API çağrısını bir müşterinin pilot dağıtımı canlıya çıktıktan üç hafta sonra bir destek biletine dönüştüren şeydir. AcroForm JavaScript makalesi, tek satırlık çağrıyı gösterir ve AcroForm-versus-XFA sonucunu bir kod yorumunda belirtir; bu makale aynı API üzerinde kalır ve dahili yazma yolunu, XFA yazmasının hiç ulaşmadığının datasets-paketi kanıtını, boşluğun neden Delphi bağlaması yerine PDFium'un kendisinde oturduğunu ve düzenlemenin bir kayıttan sağ çıkması gereken belgeler için kendi-XML'inizi-yamalayın türünden bir geçici çözümü ele alır
SetFocusedFormFieldText bir alan değerini nasıl yazar?
TPdf.SetFocusedFormFieldText, bir değeri doğrudan belge modeline dürtmek yerine, tuş vuruşu düzeyinde bir düzenlemeyi taklit ederek çalışır. Dahili olarak, odaklanılan alanın mevcut içeriğini seçmek için FORM_SelectAllText'i, ardından seçimin üzerine yeni dizeyle yazmak için FORM_ReplaceSelection'ı çağırır -klavye tabanlı bir tümünü-seç-ve-yaz'ın tetikleyeceği aynı iki işlem. Yazma, PDFium'un etkileşimli metin düzenleme yolunun etrafından değil içinden geçtiğinden, alana bağlı herhangi bir tuş vuruşu, format veya hesaplama betiği, bir insan yazıyormuş gibi tam olarak tetiklenir ki bu, API'yi JavaScript'i canlı tutan bir görüntüleyicide programatik form doldurma için kullanışlı kılan şeydir. Okuma tarafı karşılığı, FORM_GetFocusedText tarafından desteklenen FocusedFormFieldText'tir ve SetFocusedFormFieldText'in az önce yazdığı aynı canlı arabelleği yansıtır
if Pdf.FocusedFormFieldIndex >= 0 then
begin
if Pdf.SetFocusedFormFieldText('1284.50') then
Log('Buffer now reads: ' + Pdf.FocusedFormFieldText)
else
Log('No field is focused, or it does not accept text');
end
else
Log('Focus a field first - FocusFormField or a real click');
AcroForm neden değeri tutuyor da XFA neden kaybediyor?
AcroForm metin ve combo alanları kalıcıdır, çünkü PDFium'un kendi form-doldurma ortamı düzenleme arabelleğini sizin için işler: alan odağını kaybettiği anda, arabellek alanın /V girdisine yazılır, uyumlu her PDF okuyucunun bir alanın saklanan değerini bilmek için baktığı aynı anahtar. Kaputun altında FORM_ForceToKillFocus'u çağıran TPdf.ClearFormFieldFocus, bu işlemeyi talep üzerine zorlar; bu yüzden bir değeri programatik olarak ayarlayan kod, UI'de başka bir yerde gerçek bir fare tıklamasını beklemek zorunda kalmaz. Hemen ardından kaydedin ve yeni metin, /V gerçek bir alan sözlüğünde gerçek bir girdi olduğundan, sonradan eklenen bir şey değil, TPdf.SaveAs hiç çalışmadan önce belge nesne grafiğinin parçasıdır
Pdf.FocusFormField(FieldIndex);
Pdf.SetFocusedFormFieldText('1284.50');
Pdf.ClearFormFieldFocus; // forces the /V commit now
Pdf.SaveAs('invoice-acroform.pdf');
// Reopen and confirm - this is an AcroForm document, so it holds
Pdf.Active := False;
Pdf.FileName := 'invoice-acroform.pdf';
Pdf.Active := True;
Pdf.FocusFormField(FieldIndex);
Assert(Pdf.FocusedFormFieldValue = '1284.50'); // passes
Bir XFA alan düzenlemesi gerçekte nerede yaşar?
XFA alanlarında böyle bir bağlantı yoktur. Bir kullanıcının yazdığı metin, PDFium'un XFA render ve etkileşim katmanına ait bir CPWL_Edit arabelleğine iner ve o katmanın arabelleği PDF'te saklanan datasets paketine geri kopyalayan hiçbir kod yolu yoktur. TPdf.GetXfaDatasets, boşluğu görünür kılar: bir XFA alanında bir düzenlemeden önce ve sonra çağırın ve döndürdüğü baytlar aynıdır, çünkü metot belgenin açıldığı orijinal paketi okur, az önce düzenlediğiniz widget'ın canlı durumunu asla okumaz. Bunun hiçbiri bir önbellekleme hatası veya yenileme zamanlaması sorunu değildir -diskteki datasets paketi ve bellekteki düzenleme arabelleği, PDFium'un kamuya açık API'sinin hiçbir zaman bağlamadığı basitçe iki farklı durum parçasıdır
var
Before, After: TBytes;
begin
Before := Pdf.GetXfaDatasets;
Pdf.FocusFormField(FieldIndex);
Pdf.SetFocusedFormFieldText('1284.50');
After := Pdf.GetXfaDatasets;
// Before and After are byte-for-byte identical on an XFA document -
// the edit never touched the packet GetXfaDatasets reads from
end;
Bu bir PDFium Bileşeni hatası mı yoksa bir PDFium sınırlaması mı?
Eksik parça, onun üzerindeki Delphi bağlamasında değil, PDFium'un kendisinde oturur. PDFium'un kamuya açık API'sinin güncellenmiş bir paketi enjekte etmek için bir FPDF_SetXFAPacket'i ve bir kayıttan önce XFA motorundan mevcut DOM'unu datasets XML'ine geri serileştirmesini istemek için bir FPDF_SaveAsXFA'sı yoktur. TPdf.SaveAs'ı destekleyen dışa aktarım olan FPDF_SaveAsCopy, PDFium'un zaten sahip olduğu belge nesne grafiğini yazar; XFA motorundan önce canlı durumunu boşaltmasını istemek için bir kancası yoktur, çünkü o kanca akış yukarısında yoktur. PDFium Bileşeni, PDFium'un kendisinin hiç uygulamadığı bir uzlaştırmayı ekleyemez ve PDFium'un dahili XFA durumunu tahmin eden ev yapımı bir DOM-XML serileştiricisi göndermek, dürüst boşluktan daha kötü olurdu: bir sonraki PDFium sürümü, projenin dışındaki hiç kimsenin göremeyeceği bir şeyi değiştirene kadar çalışıyormuş gibi görünürdü
Bu sınır, en baştan SetFocusedFormFieldText'i kuran aynı v2.13.2 denetimi sırasında yüzeye çıktı. FORM_ReplaceSelection, sürümler boyunca hiç Pascal kodundan çağrılmadan DLL import tablosunda bağlanmıştı ve onu sonunda kullanan yazma yolunu eklemek, kalıcılık boşluğunu teorik olmaktan çıkarıp belgelenecek kadar somut hale getiren şeydi. Aynı denetim turu, ilgisiz ama ruh olarak ilgili bir boşluk daha ortaya çıkardı: AcroForm JavaScript, v2.13.0'dan beri sessizce devre dışı kalmıştı, çünkü JS platformu yalnızca XFA başlatma dalının içinde bağlanmıştı; bu yüzden app.alert veya hesaplanmış alanlara sahip sıradan AcroForm belgeleri hiç bir betik motoru almadı. Bu düzeltilebilirdi -JS platformunu XFA'dan bağımsız olarak her belgeye genişletmek- ve aynı sürümde gönderildi; burada ele alınan kalıcılık boşluğu ise yukarıdaki nedenlerle düzeltilebilir değildi. JavaScript düzeltmesi ve etrafındaki ana bilgisayar-veto olayları, PDFium Bileşeni ile AcroForm JavaScript çalıştırma makalesinde ele alınmıştır
Delphi'de bu konuda ne yapmalısınız?
AcroForm belgeleri için düzeltme iyi bir alışkanlıktan başka bir şey değildir: bir değer programatik olarak ayarlandığında, sonraki bir UI etkileşiminin sizin için işlemeyi tetikleyeceğini varsaymak yerine SaveAs'tan önce her zaman ClearFormFieldFocus'u çağırın (veya başka türlü odağı uzaklaştırın). Genel amaçlı bir görüntüleyicide yaygın durum olan ya AcroForm ya da XFA olabilecek bir belge için, bir kaydın tutacağını bir çağırana vaat etmeden önce FormType'u veya XFA boolean'ını kontrol edin ve /V'yi onurlandıran aksi halde sıradan AcroForm widget'larının üzerine XFA içeriğinin katmanlandığı XFAF durumu dahil, tam prob kümesi için XFA formlarını tespit etme ve XFA paketlerini çıkarma makalesini okuyun
Düzenlenmiş değerlerin bir kayıttan sağ çıkması gereken gerçek bir dinamik XFA formu için, etkileşimli düzenleme arabelleği hiç doğru araç değildir. Dayanıklı yol, GetXfaDatasets'i sonucunuz değil, temel çizginiz olarak ele almaktır: belge açıldığında bir kez okuyun, kullanıcının alan alan neyi değiştirdiğinin kendi kaydınızı tutun -PDFium onları olaydan sonra size geri vermeyeceğinden, tam olarak UI'nizin zaten sahip olduğu değerler- bunları kendiniz temel XML'e yamalayın ve kendi çıktınızı sürün. Kendi kodunuzun kontrol ettiği XML üzerinden geçen bir yazma, bir CPWL_Edit arabelleğinin asla sağ çıkamayacağı bir kayıttan sağ çıkar
function ExportEditedXfaValue(Pdf: TPdf; const FieldPath,
NewValue: string): TBytes;
var
DatasetsXml: string;
begin
// GetXfaDatasets ships with PDFium Component; PatchXmlNode below is
// your own helper over your own XML library, nothing PDFium provides
DatasetsXml := TEncoding.UTF8.GetString(Pdf.GetXfaDatasets);
DatasetsXml := PatchXmlNode(DatasetsXml, FieldPath, NewValue);
Result := TEncoding.UTF8.GetBytes(DatasetsXml);
end;
Boşluğu bir müşteriden önce yakalamak
TPdf.SaveAs, bir XFA alan değeri sağ çıksın çıkmasın True döndürür, çünkü PDFium'un bakış açısından kayıt gerçekten başarılı oldu -yazması istenen her baytı yazdı. Bu, bunu tam olarak bir duman testinin kaçırdığı ve bir müşteriye ulaşan türden bir kusur yapar: hiçbir şey fırlatmaz, hiçbir şey günlüğe kaydetmez, dosya sorunsuz açılır, yalnızca belirli değer yanlıştır. Kaydedilen dosyayı gerçekten yeniden açan ve alanın değerini karşılaştıran -veya daha önceki örnekte olduğu gibi GetXfaDatasets'i önce ve sonra karşılaştıran- bir gidiş-dönüş testi, yalnızca varsayılan olarak çalışan AcroForm yollarına değil, kullanıcıların XFA içeriğini düzenlemesine izin veren herhangi bir görüntüleyici için gerileme paketine aittir
Bunların hiçbiri PDFium Bileşeni'ne karşı dosyalanacak bir kusur değil, etrafında tasarım yapılacak bir sınırdır: SetFocusedFormFieldText, her iki form modeli için de adının tam olarak söylediğini yapar ve sonuçtaki fark, temiz bir şekilde AcroForm ile XFA'nın PDFium tarafında o arabelleğe her birinin neyi bağladığına iner. Burada referans verilen API, odak ve kayıt ilkelleri ve paket okuyucular, Delphi ve C++Builder için PDFium Bileşeni'nin bir parçasıdır