Deep, bir satır sonu ve Bluedan kurulu bir liste kutusu dışa aktarma değeri son derece yasal PDF'tir; Red adında ikinci bir seçenek de öyledir. İkisini seçin, seçimi takas dosyası için tek bir LF ile ayrılmış dizeye düzleştirin ve karşı taraf Deep\nBlue\nRed alır: iki seçim mi üç mü, baytlarda bunu söyleyen hiçbir şey yoktur. Multi-select değerleri FDF'den ya da XFDF'den yalnızca bütün yol boyunca dizi olarak kaldıklarında sağlam döner; PDF'ten çıktıkları andan, içine geri yazıldıkları ana kadar
Bir satır sonu birleştirmesi neden asla multi-select değeri taşıyamaz?
Birleştirilmiş bir dize, değerler arası sınırları kaybeder ve bir değerin ayırıcıyı içinde barındırmasına izin verildiği için hiçbir içe aktarıcı o sınırları geri koyamaz. ISO 32000-1 §12.7.4.4, bir choice alanının /V girdisinin tek bir metin dizesi ya da metin dizesi dizisi tutmasına izin verir; MultiSelect bayrağı taşıyan (/Ffin 22. biti) bir liste kutusu, birden fazla seçenek seçilir seçilmez dizi biçimine geçer. Aynı bölüm /Iyi tanımlar: sıfır tabanlı seçenek indekslerinin artan sırada dizilmiş bir dizisi; bir görüntüleyici, aynı dışa aktarma değerini paylaşan iki seçeneği böyle ayırt eder. Ne bir yapı ne öteki, dize tipinde bir API'den geçebilir. HotPDF'teki GetFormFieldValue bir AnsiString döndürür ve /Vnin dize biçimini okur; dolayısıyla üzerine kurulan her dışa aktarma, alan başına en fazla bir skaler yazabilir
İçe aktarma tarafında ayna görüntüsü bir sorun var. XFDF her seçimi kendi <value> elemanına yazar ve bunları skaler bir atayıcıya veren içe aktarıcı, önce onları birbirine yapıştırmak zorundadır; genellikle LF ile. Yukarıdaki Deep/Blue seçeneğinde o yapıştırıcı, veriden ayırt edilemez. HotPDF bu yüzden skalerleri gidiş dönüşün ortasından uzak tutar: ExportLoadedFormToFDF, ExportLoadedInterchangeToFDF ve ExportLoadedFormToXFDF diziyi doğrudan alandan okur; eşleşen içe aktarma metotları da birleşik bir dizeden hiç geçmeden yeni bir dizi kurar
Dışa aktarılan FDF ve XFDF dosyaları ne içerir?
Diskte sınırlar görünür kalır. FDF'de bir multi-select alan, türü belirtilmiş bir diziye dönüşür ve her öge, kaynak PDF'teki yazımını korur: onaltılık dizeler hex olarak çıkar, sabit dizeler CR ve LF \r ve \n olarak kaçırılmış sabitler olarak çıkar. ExportLoadedFormToFDFin yazdığı dosyada, alan üzerindeki zengin metin /RVsi ya da sıfırdan farklı yerel /Ff, aynı alan sözlüğünde yolculuk eder. XFDF'de kök eleman xml:space="preserve" bildirir; ISO 19444-1 bunu zorunlu kılar ve bir metin elemanının içindeki her boşluk karakterini veriye çevirir. Bu, biçimli yazdırma için bir tuzaştır. HotPDF her <value>in başlangıç etiketini, kaçırılmış metnini ve bitiş etiketini tek satıra yazar, girintiyi elemanın dışında tutar ve CR, LF ile TAB'ı karakter referansları olarak kodlar; böylece satır sonu normalizasyonu uygulayan bir XML ayrıştırıcı, CR LF çiftini tek bir LF'e geri yazamaz
<!-- FDF: alan başına bir türü belirtilmiş dizi -->
<< /T (options) /V [(Deep\nBlue) (Red)] >>
<< /T (region) /V [<45553132>] >>
<!-- XFDF: seçim başına bir <value> -->
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<fields>
<field name="options">
<value>Deep
Blue</value>
<value>Red</value>
</field>
</fields>
</xfdf>
Çağıran kodu yazarken iki dışa aktarma uç vakası önem taşır. ExportLoadedFormToFDF, tüm FDF gövdesini bellekte kurar ve hedef dosyayı ancak ondan sonra oluşturur; yani dışa aktarılamayan bir değer (dizelerin dışında bir şey tutan bir dizi gibi), diskteki önceki .fdf hâlâ sapasağlamken fırlatır. İkinci vaka, XFDF'nin kendisinde gerçek bir boşluktur: boş dize dışa aktarma değeri de sunan bir liste kutusunda boş seçim. <value/> "hiçbir şey seçilmedi" de olabilir "boş seçenek seçildi" de; biçimin üçüncü bir yazımı yoktur. ExportLoadedFormToXFDF bu durumda, hedef dosya açılmadan önce fırlatır. FDF'de böyle bir sorun yoktur; çünkü /V [] ile /V [()] farklı nesnelerdir. Üç dışa aktarıcı da /T adı olmayan yalnızca-widget uçlarını atlar; çünkü hiçbir içe aktarıcı böyle bir girdiyi bir alana geri eşleyemez
var
Pdf: THotPDF;
Written: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
begin
// Multi-select liste kutuları /V [(...) (...)] olarak yazılır
Written := Pdf.ExportLoadedFormToFDF('order-form.fdf');
try
Pdf.ExportLoadedFormToXFDF('order-form.xfdf');
except
on E: Exception do
// Boş seçim artı boş bir dışa aktarma seçeneği: XFDF ayırt edemez,
// ve mevcut .xfdf dosyasına dokunulmaz
ShowMessage('XFDF export refused: ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
end;
İçe aktarılan bir seçim /Opt'ye karşı nasıl sınanır?
HotPDF, gelen bir diziyi yalnızca hedef, MultiSelect bayrağı kurulmuş bir choice alanıyken ve her değer, alanın /Opt dizisindeki bir dışa aktarma değeriyle eşleştiğinde kabul eder (iki ögeli bir seçenek çifti için ilk öge). Her seçenek yuvası bir kez kullanılabilir. İki seçeneği de b dışa aktaran bir liste, bu yüzden [<62> <62>] değerini iki ayrı seçim olarak kabul eder ve üçüncü bir byi reddeder; onun için kullanılmamış yuva kalmamıştır. Yeniden kurulan /I, değerlerin geldiği sıraya bakmaksızın /Opt sırasını izler; bu, indeksleri §12.7.4.4'ün gerektirdiği gibi artan tutar. Yeni /V ve /I, kopuk nesneler olarak kurulur ve yalnızca her değer geçtikten sonra atanır; reddedilen bir değer alanda yarım bir dizi ya da bayat indeksler bırakamaz
Atamadan sonra alan taze bir görünüm akışı alır. Bu adım, elle yazılmış bir içe aktarıcıda unutması kolay bir adımdır ve belirtisi kafa karıştırıcıdır: görüntüleyiciler saklanan /APyi çizer, /Vyi değil; yani veri değişmiş olsa da eski seçim ekranda ve baskıda kalır. Liste kutusu ayrıca değişen-alan kümesine katılır; yani parametresiz bir RecalculateLoadedFormFieldsIncremental çağrısı, tam olarak onu okuyan hesaplamaları yeniden koşturur. FDF'den gelen hex yazımları kayda dek hex olarak kalır. Yalnızca bir skaler değer atamanız gerekiyorsa yüklenmiş bir PDF'te tek form alanı değeri atama yazısı o yolu anlatır; o yol tasarımı gereği çoklu seçimlerle uğraşmaz
Bir girdi kalıbı özel işlemi hak eder. Bir PDF, düz bir ASCII dışa aktarma değerini bayt sırası işareti olmadan bir hex dizesi olarak saklayabilir, Abr için <416272> gibi; ve PDF dize gövdesini olduğu gibi kopyalayan bir dışa aktarıcı, eleman metni çözümlenmiş metin değil de ham rakamlar 416272 olan XFDF üretir. Onaltılık kodlanmış bir dışa aktarma değerini ham rakamlarıyla taşıyan XFDF, yazımla /Opt eşleyemez: katı bir karşılaştırma 416272 adında bir seçenek bulamaz ve içe aktarma başarısız olur. HotPDF'teki XFDF içe aktarıcı böyle bir dosya aldığında, eşleşmeyen değeri bir kez, onu hex yükü olarak çözümleyerek yeniden dener. Yeniden deneme bilinçli olarak dardır. Yalnızca hiç bir eşleşme bulamayan bir değer için koşar; her tam eşleşme önce ayrılır, böylece kurtarılan bir yazım asla sonradan gelen, tam yazımlı bir değerin ihtiyaç duyacağı bir seçenek yuvasını alamaz; ve saklanan /V ögesi seçeneğin kendi PDF dizesidir, yani /V ile /I aynı seçimi tarif eder. Hoşgörünün doğru dozu budur: yalnızca içe aktarmanın aksi hâlde fırlatacağı yerde devreye girer ve zaten eşleşmiş bir değer asla yeniden yorumlanmaz. Eşleştirmenin kendisi, PDFDocEncoding'i, iki bayt sırası işaretli UTF-16'yı ve UTF-8'i anlayan tek bir Unicode çözümleyicisinden geçer; böylece aynı mantıksal metin, kodlamaları karışık belgelerde bile aynı seçeneği eşler
İki FDF ayrıştırma tuzağı: hex sonlandırıcılar ve yabancı nesne numaraları
İlk tuzak söz dizimseldir. << /T (region) /V <416273>>> içinde ilk > hex dizesini kapatır, sonraki ikisi sözlüğü kapatır. Yalnızca >> çiftlerine bakan bir tarayıcı, sözlüğü bir karakter erken kapatır ve alanı kaybeder. HotPDF'in dosya düzeyindeki FDF içe aktarıcısı, /Fields dizisini dilimlerken bir hex dizesinin içinde mi yoksa bir sabitin içinde mi olduğunu izler; ImportLoadedInterchangeFromFDFin arkasındaki akış tabanlı okuyucu tüm dosyayı PDF söz dizimi olarak token'lara ayırır, yani ikisi de naive bir taramanın iki gördüğünde üç kapanış ayracı görür
İkinci tuzak referanslarla ilgilidir. Bir FDF dosyası, kendi nesne numaralandırmasına sahip küçük bir PDF-söz dizimi belgesidir (ISO 32000-1 §12.7.7); yani /V [11 0 R] FDF dosyasının 11 numaralı nesnesidir, doldurulan PDF'in değil. Onu hedef belgeye göre çözümlemek, o numarayı orada tesadüfen taşıyan hangi nesne varsa sessizce içeri çeker. Delphi 2009 ve sonrası derlemelerde değişim okuyucusu FDF'yi tam bir nesne grafiği olarak çözümler, referansı o grafiğin içinde izler, ortaya çıkan dizeleri her satır içi dizi gibi doğrular ve FDF 11 nesnesini içermiyorsa fırlatır. Dosya düzeyindeki ImportLoadedFormFromFDF her alan sözlüğünü, FDF'nin nesne tablosu olmadan kendi başına okur; referanslardan kurulu bir dizi onun yalnızca-dizeler denetimini geçemez ve o alan atlanır
İçe aktarma yolu seçmek: atla, fırlat ya da geri al
Üç içe aktarma yolu da multi-select değerlerini aynı şekilde doğrular. Fark, bir değer başarısız olduğunda olan şeydedir. ImportLoadedFormFromFDF başarısız olan her alanı atlar ve uyguladığı alan sayısını döndürür; yani gönderdiğiniz girdi sayısından düşük bir sayı, sorunun tek işaretidir. ImportLoadedFormFromXFDF ile ImportLoadedInterchangeFromFDF ilk reddedilen alanda fırlatır ve her çağrı, yüklü belge üzerinde tek bir işlem gibi koşar: istisna metottan çıktığında, çağrının o ana kadar değiştirdiği her alan (değişim yolunda her anotasyon da) geri alınır. False döndüren bir değişim içe aktarması da geri alınır; Status ve info kaydı temizlenir
Bir insanın gözden geçirip geri gönderdiği veri için fırlatan yollardan birini kullanın. Yarım uygulanmış bir sipariş formu, reddedilmiş kinden kötüdür; çünkü kaydedilen PDF'te hangi liste kutularının gözden geçirilmiş dosyadan geldiğini, hangilerinin eski değerlerini koruduğunu gösteren hiçbir şey yoktur. ImportLoadedFormFromFDFin atla-ve-say davranışı başka bir işe uyar: formun az farklı bir revizyonu için üretilmiş olabilecek bir FDF'den elinden geleni yapan ön doldurma
var
Pdf: THotPDF;
Source: TMemoryStream;
Status: AnsiString;
Info: THPDFFDFInterchangeInfo;
begin
Pdf := THotPDF.Create(nil);
Source := TMemoryStream.Create;
try
Source.LoadFromFile('order-form-reviewed.fdf');
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
try
// Yalnızca alanlar. /Opt dışında bir değer ya da tek seçimli bir
// listeye yönelik birden fazla değer, tüm içe aktarmayı fırlatıp geri alır
if Pdf.ImportLoadedInterchangeFromFDF(Source, True, False, Status, Info) then
begin
// Yalnızca içe aktarılan alanları okuyan hesaplamaları yeniden koştur
Pdf.RecalculateLoadedFormFieldsIncremental;
Pdf.SaveLoadedDocument('order-form-filled.pdf');
end;
except
on E: Exception do
ShowMessage('Import rejected, document unchanged: ' + E.Message);
end;
finally
Source.Free;
Pdf.Free;
end;
end;
Kendi veri deponuzu XFDF dizi callback'lerine bağlamak
XFDF ile callback'lerle tarif edebildiğiniz her alan deposu arasında dönüşüm yapan HPDFXFDF unit'i, dizi desteğini ayrı bir kayıtta, THPDFXFDFArrayAccess içinde ve HPDFXFDFExportFields ile HPDFXFDFImportFieldsin ek overload'larında taşır. Mevcut THPDFXFDFAccess kaydına yeni yuvalar eklemek daha kısa ve yanlış olurdu. Çağıranlar o kaydı sık sık yerel bir değişken olarak bildirir ve yalnızca bildikleri yuvaları doldurur; sondaki yeni bir fonksiyon işaretçisi, kütüphanenin callback sanacağı stack çöpü taşırdı. Ayrı kayıtla mevcut kod düzenini ve overload'larını korur; o overload'lar içeride hep-nil bir dizi kaydı geçirir
İki içe aktarma overload'ı, tekrarlanan <value> elemanlarında farklı davranır. Özgün skaler overload onları hâlâ LF ile birleştirir ve SetFormFieldValue çağırır; mevcut çağıranlar o teamüle bağlıdır. Dizi farkında olan overload asla birleştirmez: listeyi SetFormFieldValueArraye verir, yalnızca callback'inizin reddettiği tek bir değer için skaler atayıcıya döner ve callback'iniz iki ya da daha fazla ögeli bir listeyi reddettiğinde fırlatır. Dışa aktarmada Default(THPDFXFDFArrayAccess)den başlayın; atamadığınız yuvalar (isteğe bağlı SetFormFieldRV dahil) nil kalsın. GetFormFieldValueArrayden, hiçbir şey seçilmemiş olan dahil, liste değerli her alan için True döndürün; alanı skaler callback'e bırakmak için False. Boş bir dizi tek bir boş <value/> olarak çıkar; bu yüzden boş dize seçeneği sunan bir depo bu vakayı callback'in içinde reddetmeli, tıpkı ExportLoadedFormToXFDFin yaptığı gibi
uses HPDFXFDF;
// Düz fonksiyon işaretçisi, "of object" değil: Context sizin deponuzu taşır.
// TFormStore ve metotları sizin kodunuz, HotPDF'in parçası değil
function StoreGetSelections(Context: Pointer; FieldIndex: Integer;
out Values: THPDFXFDFValueArray): Boolean;
begin
Result := TFormStore(Context).IsListField(FieldIndex);
if Result then
Values := TFormStore(Context).Selections(FieldIndex);
end;
procedure ExportStore(Store: TFormStore; out Bytes: TBytes);
var
Access: THPDFXFDFAccess;
ArrayAccess: THPDFXFDFArrayAccess;
begin
Access := MakeStoreAccess(Store); // mevcut skaler bağlarınız
ArrayAccess := Default(THPDFXFDFArrayAccess); // kullanılmayan her yuva nil
ArrayAccess.GetFormFieldValueArray := StoreGetSelections;
HPDFXFDFExportFields(Access, ArrayAccess, Bytes);
end;
Yukarıdaki her şey, hâlihazırda var olan ve MultiSelect bitini /Ff içinde kurulmuş taşıyan liste kutularını varsayar. Choice alanları oluşturmak ve bayrak bitlerini kurmak yüklenmiş bir PDF'e ListBox ve diğer AcroForm alanlarını ekleme yazısında; XFDF'nin <annots> kısmından geçen yorum işaretlemesi ise HotPDF'de XFDF anotasyon içe ve dışa aktarma yazısında ele alınır. Burada gösterilen her çağrının metot referansı ve kendi formlarınızda koşturmak için bir deneme derlemesi HotPDF Delphi PDF component sayfasında