Hemen her PDF görüntüleyicinin alan listesinde, Almancalı bir talep formundaki sokak alanı Straße okur. /T girişinin ardındaki baytlar bambaşka bir şey anlatır: <FEFF005300740072006100DF0065>, önünde byte order mark taşıyan onaltılık bir UTF-16BE dizisi. Alan adlarını o ham baytlarla eşleyen kod, alanı yok sayar; oysa bir kullanıcı içine doğrudan tıklayabilir. Aynı form, bir checkboxın açık durumunu Yes yerine 2 olarak ve ülke listesini [export display] çiftleri olarak saklıyor olabilir; bu da yazdığınız değerle görüntüleyicinin boyadığı değerin çelişmesine üç ayrı yol verir
Üç varyasyon da aynı yerden çıkar: müşterinin elindeki hangi araçla yıllar önce kurulmuş bir form; Delphi uygulamanızın şimdi bir veritabanından doldurup her okuyucuda aynı görünen bir dosya olarak geri vermesi gereken o form. Formu yeniden kurgulayamazsınız; dolayısıyla doldurma kodu, formun yazıldığı kurallara uymak zorundadır. ISO 32000-1in üç durum için de bir kuralı vardır: metin dizileri için §7.9.2.2, düğme görünüm durumları için §12.7.4.2.3, seçim alanları için §12.7.4.4teki Tablo 231. Henüz var olmayan alanları oluşturmak başka bir iştir; Delphi'de yüklenmiş bir PDFe AcroForm alanları ekleme yazısı onu kapsar
Alanı bulmak: HotPDF, adları eşleştirmeden önce /Tyi çözümler
THotPDF.SetFormFieldValuenin iki overloadu vardır: biri sıfır tabanlı alan indeksi, biri tam nitelikli ad alır ve ikisi de bileşenin ilk kullanımda AcroForm ağacından kurduğu bir ad tablosuna göre çözümlenir. O tablo kurulurken her kısmi ad bir çözümlayiciden geçer. Onaltılık bir /T dizisi baytlara geri çevrilir; baytlar FE FF ile başlıyor ve uzunlukları çiftse kalanı UTF-16BE okunup UTF-8 olarak yeniden kodlanır. Çözümlenen her kısmi ad, §12.7.3.1in anlattığı gibi noktayla ebeveynine eklenir; böylece Address adlı bir ebeveynin altındaki City adlı çocuk alan Address.City olarak kaydedilir. Çözümlenen yalnızca arama anahtarıdır. Dosyadaki /T nesnesi onaltılık yazımını korur ve belgeyi kaydetmek, yalnızca doldurduğunuz bir alanın kimliğine dokunmaz
Anahtarlar tabloya girmeden önce küçük harfe çevrilir; böylece 'address.city', Address.Cityyi bulur. Bu, alan adlarını büyük/küçük harfe duyarlı sayan standardın ötesindedir ve yalnızca harf durumunda farklı iki alanın aramada çakışacağı anlamına gelir. Birinin elle yazdığı bir yapılandırma dosyasından alan adı okuyan kod için, kaçan bir büyük harf, tek bir formda yalnızca harf durumuyla ayrılan iki alandan çok daha olağan bir başarısızlıktır
Çözümlenen adlar, bir AnsiString içinde taşınan UTF-8 baytlarıdır ve Delphi'nin kendi dizgi dönüşümleri tam burada işe karışır. Bir AnsiString parametresine geçilen 'Applicant.Straße' gibi bir literat, sistem ANSI kod sayfası üzerinden çevrilir; ß, UTF-8 ikilisi C3 9F yerine tek bayt DF olarak gelir ve arama ıskalar. ASCII adlar etkilenmez. Başka her şey için alanları bir kez numaralandırın, her adı UTF8ToString ile çözümleyin ve indeks overloaduyla yazın:
// FindFieldIndex kendi ekleyeceğiniz bir yardımcı: çözümlenmiş UTF-8
// adlarını bir Unicode dizgiyle karşılaştırır; böylece literatinizle
// aramanın arasına ANSI kod sayfası girmez
function FindFieldIndex(Pdf: THotPDF; const Name: string): Integer;
var
I: Integer;
begin
for I := 0 to Pdf.GetFormFieldCount - 1 do
if SameText(UTF8ToString(Pdf.GetFormFieldName(I)), Name) then
Exit(I);
Result := -1;
end;
var
Pdf: THotPDF;
Street: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('claim-form.pdf') <= 0 then Exit;
// ASCII adlar doğrudan adla çalışan overloada gidebilir
Pdf.SetFormFieldValue('Applicant.FullName', 'Maria Schneider');
Street := FindFieldIndex(Pdf, 'Applicant.Straße');
// Değer de bir AnsiStringtir; 'ß' literatı sistem kod sayfasına
// bağımlı olurdu; FEFF hex (Hauptstraße 12) bunu önler
if Street >= 0 then
Pdf.SetFormFieldValue(Street,
'FEFF00480061007500700074007300740072006100DF0065002000310032');
// Aynı FEFF önekli UTF-16BE hex, adla çalışan overloadda da
// işler (bu München); hex dizisi olarak yazılır
Pdf.SetFormFieldValue('Applicant.City', 'FEFF004D00FC006E006300680065006E');
Pdf.SaveLoadedDocument('claim-form-filled.pdf');
finally
Pdf.Free;
end;
end;
SetFormFieldValue adım adım ne yazar?
İki overload da alan bulunduktan sonra aynı sırayı işletir: /V yaz, seçim alanı indeksini yeniden kur, alanı değişmiş işaretle, düğme görünüm durumlarını değiştir, metin ya da seçim görünümünü yeniden üret ve alanı bir dirty kümesine kaydet. Son adım, parametersız RecalculateLoadedFormFieldsIncrementali besler; o da yalnızca değişen bir alanı okuyan hesaplama scriptlerini yeniden çalıştırır - doğrudan ya da kalem, ara toplam, toplam gibi bir zincir üzerinden
Yeni /Vnin nesne türü alana bağlıdır. Bir checkbox ya da radio button için SetFormFieldValue daima bir name nesnesi yazar; çünkü §12.7.4.2.3 seçili durumu bir ad olarak saklar ve PDF adları, metin-dizisi kodlaması olmayan düz baytlardır. Bütün öbür alanlar bir dizgi nesnesi alır ve geçtiğiniz değer önce incelenir. FEFF ile başlayan, uzunluğu çift olan ve yalnızca hex rakamları içeren bir dizgi, §7.9.2.2deki UTF-16BE biçimi sayılır ve <FEFF...> olarak serileştirilir. Yukarıdaki City satırının dayandığı mekanizma budur. Başka her şey, verdiğiniz baytlarla bir literal dizgi olarak saklanır; yani düz Latin metni düz metin olarak girer
Değer değiştikten sonra bir checkbox neden eski işaretini korur?
Bir düğme alanında değer, ne çizileceğine karar vermez. Bir checkbox widgetı, /AP /Nin hangi akışının o anda göründüğünü adlandıran bir /AS girişi taşır ve görüntüleyiciler /Vden değil /ASden boyar. /AS hâlâ Off derken /Vyi Yese çevirin ve dosya kendisiyle çelişir; bir flattenıcı, form verisi işaretli derken işaretsiz kutuyu sayfaya pişirir. SetFormFieldValue bu yüzden düğme alanını ve /Kids dizisindeki her girişi ziyaret eder, her widgetın /AP /Nsinden açık durum adını okur ve /ASyi eşleşmede o ada, aksi hâlde Offa çeker
Gerçek formlar o kuralı iki yerden büker. Kapalı görünümü standart Off adlandırır; yine de yazma araçları çoğu zaman o akışı tümüyle dışarıda bırakır ve görüntüleyiciye hiçbir şey çizdirtmez, dolayısıyla tek girişli bir normal görünüm sözlüğü tamamen meşrudur. Açık durum adı, yazarın seçtiği herhangibir şeydir: Yes, On, 2, formun kendi dilinde bir kelime. Bu yüzden açık durum, /AP /Nin Off olmayan ilk anahtarı olarak alınır ve değerinle karşılaştırma büyük/küçük harfi yok sayar. Radio buttonlar §12.7.4.2.4teki düzeni ekler: seçili değer, ebeveyn alanın /Vsinde yaşar; her çocuk widgetın genellikle kendi /Vsi yoktur. Her çocuk, /Parent zinciri üzerinden devralınan değerle karşılaştırılır
// Checkbox: değer, /AP /N içindeki açık durum anahtarına eşit olmalı
// (burada 'Yes'; formu tasarlayan '2' ya da 'On' da seçmiş olabilir)
Pdf.SetFormFieldValue('Consent', 'Yes');
// Radio grubu: /V, ebeveyne bir ad olarak yazılır; her çocuk widget
// /ASyi kendi açık durum adına ya da Offa çeker
Pdf.SetFormFieldValue('PaymentMethod', 'Card');
// Checkbox temizlemek: hiçbir açık durumla eşleşmeyen değer /AS Off verir
Pdf.SetFormFieldValue('Newsletter', 'Off');
Ters yüzü şu: bir yazım hatası exception üretmez. Sonunda boşluklu 'yes ' hiçbir şyle eşleşmez; kutu Offta kalır ve dosya hâlâ tamamen geçerlidir. Değerleriniz bir veritabanı kolonundan geliyorsa çağrıdan önce kırpın
Seçim alanları: /I ile /Vyi uyumda tutmak
Bir combo box ya da list box seçimini iki yerde kaydeder. /Vnin yanında Tablo 231, /Iyi /Opt içine sıfır tabanlı indeksler dizisi olarak tanımlar; /Isi 0. seçeneği gösterirken /Vsi 3. seçeneği adlandıran bir görüntüleyici, yanlış satırı vurgulayabilir. SetFormFieldValue, bir seçim alanına yapılan her çağrıda /Iyi yeniden kurar. Her şeyden önce alanın kendi /I girişini siler ve eski diziye asla yerinde dokunmaz: dolaylı bir dizi başka bir alanla paylaşılıyor olabilir ve onu değiştirmek o alanın seçimini de değiştirirdi. Ardından /Opti /Parent zinciri üzerinden çözer - seçenekler devralınmış olabilir - ve girişleri gezer. Çıplak bir dizgi seçeneği doğrudan karşılaştırılır; bir [export display] çifti export elemanı üzerinden karşılaştırılır ve iki elemandan az taşıyan bir çift atlanır
Karşılaştırma çözümlenmiş metin üzerinde koşar; böylece PDFDocEncodingde saklanan bir seçenek, UTF-16BE hex olarak yazılan bir değerle eşleşir ve tersi. Skalar bir değer en çok bir indeks üretir; alanın önce taşıdığı hangi çoklu seçim varsa değiştirilir. Hiçbir şey eşleşmediğinde hiç /I yazılmaz. Düzenlenebilir bir combo box için doğru sonuç budur; çünkü §12.7.4.4, kullanıcının liste dışında bir değer yazmasına izin verir ve o değerin işaret edeceği bir indeks yoktur
Combo boxlarla ilgili en yaygın şikâyetin arkasında da aynı kural vardır: çağrı başarılı olur, /V yeni metni taşır ve görüntüleyici seçili hiçbir şey göstermez. Genellikle çağıran, [CA Canada] çiftlerinden kurulu bir listeye görüntü etiketini (Canada) geçmiştir. Bileşen, düzenlenebilir comboları bozmadan bunu hata sayamaz; kontrol sizin kodunuza düşer. GetFormFieldOptionValue, her seçeneğin export elemanını döndürür ve bir kontrol kısa olur:
// SetChoiceByExport kendi kontrolünüzdür, bileşenin parçası değildir:
// /Optte hiçbir export elemanıyla eşleşmeyen değerleri reddeder
procedure SetChoiceByExport(Pdf: THotPDF; const FieldName,
ExportValue: AnsiString);
var
I: Integer;
begin
for I := 0 to Pdf.GetFormFieldOptionCount(FieldName) - 1 do
if Pdf.GetFormFieldOptionValue(FieldName, I) = ExportValue then
begin
// /Opt [[US United States] [CA Canada] [MX Mexico]] ise:
// 'CA' export elemanıyla eşleşir ve /I [1] olur
Pdf.SetFormFieldValue(FieldName, ExportValue);
Exit;
end;
raise Exception.CreateFmt('%s is not an export value of %s',
[string(ExportValue), string(FieldName)]);
end;
// Serbest metinli düzenlenebilir combo: doğrudan yazın. /V ayarlanır,
// /I silinir ve hiçbir indeks uydurulmaz
Pdf.SetFormFieldValue('Title', 'Principal Engineer');
Yeni değer /NeedAppearances olmadan görünür mü?
Metin ve seçim alanları için evet. Bir görüntüleyici saklanan /AP akışını çizer ve /Vye bakmaz; dolayısıyla eski görünümü taşıyan yeni bir değer, AcroForm sözlüğü /NeedAppearances true taşımadıkça (§12.7.3.3) ve görüntüleyici ona uyumayı seçmedikçe eski metni ekranda da baskıda da bırakır. Flattenıcılar ve thumbnail üreticileri o bayrağı sık sık yok sayar. SetFormFieldValue, metin ve seçim alanlarının görünüm akışını çağrının parçası olarak yeniden üretir; genel erişimli EnsureLoadedFieldAppearanceStreamin kullandığı kurucunun aynısıyla. Akış, devralınan /DA dizgisinden, /Q hizalamasından, comb alanları için /MaxLenden ve değerin kendisinden kurulur. /DAda adı geçen font, AcroForm /DR kaynakları üzerinden çözülür; böylece bir Type0 font, Helveticaya düşmek yerine kendi gliflerini korur. Düğmeler, tasarımcılarının çizdiği durum görünümlerini korur; onlar için yalnızca /AS oynar
SetFormFieldValue bir proceduredir ve bir şey boyanıp boyanmadığını söylemez. Kullanılabilir bir Recti olmayan widget akış almaz ve yetim ya da dikdörtgensiz widgetlı bozuk formlar gerçekten vardır. Çizilen sonuç önem taşıdığında, değeri ayarladıktan sonra EnsureLoadedFieldAppearanceStreami kendiniz çağırın. En az bir widget akış aldığında True döndürür. GetFormField size, çağrının beklediği Index özelliğini taşıyan bir THPDFLoadedFormField verir; o nesnenin sahibi sizsiniz ve onu serbest bırakmak zorundasınız
var
Field: THPDFLoadedFormField;
begin
Pdf.SetFormFieldValue('Applicant.FullName', 'Maria Schneider');
Field := Pdf.GetFormField('Applicant.FullName');
try
// False, bu alanın hiçbir widgetının boyanacak bir dikdörtgeni
// olmadığı demektir; değer saklanır ama onu gösterecek bir şey yoktur
if not Pdf.EnsureLoadedFieldAppearanceStream(Field.Index) then
raise Exception.Create('No widget rectangle to paint into');
finally
Field.Free;
end;
Pdf.SaveLoadedDocument('claim-form-filled.pdf');
end;
/Vnin değiştiğini kontrol etmek, bir kullanıcının ne göreceği hakkında pek az şey kanıtlar. Anlamlı bir test, sayfayı önce ve sonra render eder ve widget dikdörtgeninin içindeki piksellerin değişirken dışındakilerin değişmediğini teyit eder. O kontrolü elimizde tutuyoruz; çünkü yalnızca görünüm baytları bizi daha önce yanılttı: bir akış her doğru baytı taşıyordu, /NeedAppearances truee uyan bir görüntüleyici ekranı /Vden yeniden kurdu ve hiçbirini göstermedi
Değerleri geri okumak ve genellikle sıradaki adım
GetFormFieldValue, alanın devralınan /Vsini döndürür. FEFF önekli hex olarak saklanan bir değer, UTF-8e çözümlenmiş olarak geri gelir; yukarıdaki City alanı Münchenin UTF-8 baytları olarak okunur ve UTF8ToString onu bir Delphi dizgisine çevirir. Literal bir dizgi, saklandığı baytlarla geri gelir. Gidiş dönüş kontrolleri için, geçtiğiniz yazım yerine iki tarafta da çözümlenmiş metni karşılaştırın
Alan verisini form ile harici bir sistem arasında, alan başına bir çağrı yerine toplu taşımak, Delphi'de XFDF içe ve dışa aktarma yazısının konusudur. Bitmiş bir formu kimse düzenleyemesin diye kilitlemek ise Delphi'de AcroForm ve XFA alanlarını düzleştirmeydir; tam da burada anlatılan /AS durumlarını ve görünüm akışlarını statik sayfa içeriğine çevirir. O anda tutarsız olan her şey sonsuza dek tutarsız kalır; o yüzden önce doldurun, sonra kontrol edin, en son düzleştirin
SetFormFieldValue, EnsureLoadedFieldAppearanceStream ve artımlı yeniden hesaplama APIsi, HotPDF Delphi Componentin parçasıdır; Delphi ya da C++Builderdan yüklediğiniz bir belgede alanları yeniden adlandıran, kaldıran ve düzleştiren çağrıların yanı sıra