Teknik Makale

Multi-select PDF alanları: FDF ve XFDF gidiş dönüşü (Delphi)

HotPDF, multi-select liste kutusu değerlerini FDF ve XFDF üzerinden, alan değerini baştan sona bir dizi olarak tutarak gidiş dönüşe sokar. 2.755.0 sürümünden bu yana ExportLoadedFormToFDF, ExportLoadedInterchangeToFDF ve ExportLoadedFormToXFDF, her seçili seçeneği kendi FDF dizisi ya da XFDF <value> elemanı olarak yazar ve eşleşen içe aktarma metotları, her değeri alan seçeneklerine karşı kontrol edip /I seçim indekslerini hiçbir şeyi değiştirmeden önce yeniden kurar. Yolda hiçbir şey tek bir dizeye yapıştırılmaz

Bunun düzelttiği başarısızlığı yeniden üretmek kolaydır. Ürün seçeneklerinden multi-select bir liste kutusu taşıyan bir sipariş formu alın, bir kullanıcının ikisini seçmesine izin verin, form verisini bir arka ofis sistemi için dışa aktarın, sonra düzenlenmiş dosyayı PDF'e geri içe aktarın. Bu değişiklikten önce liste kutusu boş ya da yanlış dönüyordu. Sebep: dışa aktarma değerlerinden biri satır sonu içeriyordu ve eski yol seçimleri tek bir satır-ayrıklı dizeye düzleştirmişti. O diziden çoklu seçimleri geri çıkarmak hiçbir zaman güvenilir değildi ve kendi içinde satır sonu taşıyan bir dışa aktarma değeriyle hiç çalışamaz

Multi-select değerlerini satır sonlarıyla birleştirmek gidiş dönüşü neden bozar?

Seçimleri tek bir dizeye birleştirmek değerler arasındaki sınırları atar ve bir değer ayırıcıyı içerebilir; hiçbir içe aktarıcı dizeyi doğru biçimde geri bölemez. ISO 32000-1 §12.7.4.4, bir choice alanının /V girdisinin ya tek bir metin dizesi ya da metin dizeleri dizisi olmasına izin verir ve MultiSelect bayraklı (/Ffnin 22. biti) bir liste kutusu, birden fazla seçenek seçildiği anda dizi biçimini kullanır. Aynı bölüm /Iyi artan sırada sıfır tabanlı seçenek indeksleri dizisi olarak tanımlar; görüntüleyiciler bunu, kazara aynı dışa aktarma değerini paylaşan iki seçeneği ayırt etmek için kullanır. HotPDF'de skaler getter olan GetFormFieldValue yalnızca dize biçimini okur; dizi ondan geçince dışa aktarma boş dizeye düşüyordu ve eski XFDF içe aktarımı yinelenen <value> elemanlarını LF ile birleştiriyordu. Deep, satır besleme, Blue olarak dışa aktarılmış bir seçenek düşünün: birleştirmeden sonra Deep\nBlue\nRed iki seçim de olabilir üç de; dosya hangisi olduğunu söylemenin bir yolunu vermez. Düzeltme, gidiş dönüşün ortasında bir skaleri kullanmayı tümüyle bırakmaktı

Eski HotPDF multi-select gidiş dönüşü: seçilmiş iki liste kutusu seçeneğinden biri gömülü satır sonu taşırken skaler GetFormFieldValue yolu onları Deep, satır besleme, Blue, satır besleme, Red tek dizesine düzleştirir; sonraki okuyucular bunu iki seçim ya da üç seçim olarak ayrıştırabilir
Multi-select değerlerini tek dizeye birleştirmek değer sınırlarını yok eder ve kendi içinde satır sonu taşıyan bir dışa aktarma değeri düzleştirilmiş biçimi belirsiz kılar

Dışa aktarılan FDF ve XFDF dosyaları ne içerir?

HotPDF, multi-select değerini FDF'te türü belirtilmiş bir dizi olarak ve XFDF'te seçim başına bir <value> elemanı olarak yazar; böylece sınırlar diskte görünür kalır. FDF'de her öge, kaynak PDF'teki yazımını korur: onaltılık dizeler hex olarak çıkar ve literal dizeler, CR ile LFyi \r ve \ne çeviren tek bir yardımcı tarafından kaçırılır. XFDF'te kök, ISO 19444-1'in istediği xml:space="preserve" taşır; bu, bir metin elemanı içindeki her boşluğun veri sayıldığı anlamına gelir. HotPDF bu yüzden her <value>nin açılış etiketini, kaçırılmış metnini ve kapanış etiketini tek parça yazar, girintiyi elemanın dışında tutar ve CR, LF ile TABi karakter referansları olarak kodlar; böylece satır sonu normalizasyonu uygulayan bir XML parser özgün baytları değiştiremez

HotPDF'in 2.755.0'dan bu yana bir multi-select liste kutusu için yazdığı dışa aktarma biçimleri: FDF alan başına /V [(Deep satır sonu Blue) (Red)] taşıyan türü belirtilmiş bir dizi ve hex bir region değeri taşır; XFDF ise xml:space preserve altında seçim başına bir value elemanı taşır, böylece boşluk veri sayılır
Sınırlar diskte görünür kalır: FDF her seçimi kendi dizi ögesi olarak tutar ve XFDF her birini ayrı bir value elemanına yazar; hiçbir içe aktarıcının tahmin etmesi gerekmez
<!-- 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&#xA;Blue</value>
      <value>Red</value>
    </field>
  </fields>
</xfdf>

Çağıran kodu yazmadan önce bilinmeye değer iki dışa aktarma uç vakası var. Birincisi, ExportLoadedFormToFDF hedef dosyayı oluşturmadan önce tüm FDF gövdesini bellekte kurar (2.755.1'de düzeltildi); dizeler dışında bir şey tutan bir dizi gibi dışa aktarılamayan bir değer, mevcut bir dosyayı kırpmadan istisna fırlatır. İkincisi, boş dize dışa aktarma değeri de sunan bir liste kutusunda boş seçim XFDF'te belirsizdir; çünkü <value/>, hiçbir şeyin seçilmediğini ya da boş seçeneğin seçildiğini bildirebilir. ExportLoadedFormToXFDF o vakada tahmin etmek yerine istisna fırlatır ve hedef dosya açılmadan önce fırlatır. FDF'de böyle bir belirsizlik yoktur; /V [] ile /V [()] ayrıdır. İki FDF dışa aktarıcısı da /T adı taşımayan yalnızca-widget terminalleri atlar; XFDF dışa aktarıcısıyla aynı şekilde, çünkü hiçbir içe aktarıcı o girdileri 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;

HotPDF içe aktarımda bir multi-select değerini nasıl doğrular?

HotPDF, içe aktarılan bir diziyi yalnızca hedef MultiSelect bayrağı set edilmiş bir choice alanı olduğunda ve dizideki her değer, alanın /Opt dizisindeki bir dışa aktarma değeriyle eşleştiğinde kabul eder. Her seçenek yuvası bir kez kullanılabilir; dışa aktarma değeri byi paylaşan iki seçenekli bir liste [<62> <62>]i iki ayrı seçim olarak kabul eder ve üçüncü byi reddeder. Yeniden kurulan /I, /Opt sırasını izler, gelen değerlerin sırasını değil; §12.7.4.4 artan indeksler ister. HotPDF yeni /V ile /Iyi kopuk nesneler olarak kurar ve yalnızca her değer doğrulamayı geçtikten sonra atar; reddedilen bir değer asla yarım bir dizi ya da bayat indeksler bırakmaz. Kopya, paylaşılan bir ata dizisine değil içe aktarılmakta olan alana yazılır, FDF'ten gelen hex yazımları kayıt boyunca hex kalır ve hesaplamaları liste kutusuna bağımlı alanlar yeniden hesaplama için işaretlenir. Yalnızca tek bir değer ayarlamanız gerekiyorsa, yüklü bir PDF'te tek form alanı değeri ayarlamak skaler yoldan gider; o da tasarımdan çoklu seçimleri işlemez

HotPDF multi-select değerleri için içe aktarma doğrulaması: hedef /Ff'te MultiSelect set edilmiş bir choice alanı olmalı, her gelen değer her yuva bir kez kullanılmak üzere bir /Opt dışa aktarma değeriyle eşleşmeli, /I /Opt sırasında artan olarak yeniden kurulmalı ve kopuk /V ile /I yalnızca tüm değerler geçtikten sonra atanmalı
Her gelen değer, bir şey yazılmadan önce alan seçeneklerine karşı sınanır; reddedilen bir değer asla yarım bir dizi ya da bayat seçim indeksleri bırakmaz

Bazı başka araçlar, sade ASCII dışa aktarma değerlerini bayt sırası işareti olmadan hex dizeler olarak yazar, örneğin <416272>, sonra XFDF'i o hex basamaklarını metin olarak yazarak dışa aktarır. Geri yolda katı bir literal karşılaştırma başarısız olur ve içe aktarım durur. 2.755.1 sürümü bir yeniden deneme ekler: bir değer hiçbir seçenekle eşleşmediğinde HPDFHexSpellingText metni hex verisi olarak çözer ve sonucu yeniden karşılaştırır. Yeniden deneme yalnızca aksi hâlde istisna fırlatacak girdiye uygulanır; zaten eşleşmiş bir değeri asla değiştirmez. Aynı sürüm ayrıca skaler ve dizi yollarının aynı Unicode decoderi kullanmasını sağladı; bu decoder PDFDocEncodingi, her iki bayt sırası işaretli UTF-16yı ve UTF-8i anlar. Öncesinde, kodlamaları karıştıran belgelerde tek bir mantıksal değer bir yolda eşleşip öbüründe başarısız olabiliyordu

Geçerli bir FDF dosyası ayrıştırma sırasında hâlâ nasıl alan kaybedebilir?

Onaltılık dizeleri izlemeyen bir FDF tarayıcısı, bir hex değer sözlük sonlandırıcısının hemen yanında bittiğinde bir alan sözlüğünü ortadan kesebilir. << /T (region) /V <416273>>> içinde ilk > hex dizeyi kapatır ama naif bir tarayıcı onu, sözlüğün sonu olarak bir sonraki > ile birlikte okur ve alanı sessizce düşürür. Dosya düzeyindeki FDF içe aktarıcısı hâlâ hex dize içinde olup olmadığını izliyordu; 2.755.1'de ImportLoadedInterchangeFromFDFin ardındaki dizi ve sözlük tarayıcıları da aynısını yapar. İkinci konu dolaylı referanslara dairdir. Bir FDF dosyası, kendi nesne numaralandırmasına sahip küçük bir PDF-sözdizimi belgesidir (ISO 32000-1 §12.7.7); dolayısıyla /V [11 0 R] gibi bir değer, doldurduğunuz PDF'in 11. nesnesini değil FDF dosyasının 11. nesnesini anlatır. HotPDF'in sadeleştirilmiş FDF parserı dosya içindeki referansları çözmez; bu yüzden böyle bir diziyi, 11. nesnenin hedef belgede neye denk geleceğine bakmak yerine reddeder

Dosya, akış ve XFDF içe aktarımları hataları farklı bildirir

Üç içe aktarma yolu aynı şekilde doğrular ama başarısızlıkları farklı bildirir; birini bilinçli seçmeye değer. ImportLoadedFormFromFDF, doğrulamadan geçemeyen her alanı atlar ve uyguladığı alan sayısını döndürür; beklenenin altında bir sayı, sorunun tek işaretidir. ImportLoadedInterchangeFromFDF ile ImportLoadedFormFromXFDF ilk reddedilen alanda istisna fırlatır. Her alan kendi başına işlenir; istisyondan önce işlenen alanlar yeni değerlerini korur. Bunların hiçbirini tüm takas dosyası üzerinde bir transaction saymayın: hepsi ya da hiçbiri davranışı gerekiyorsa, istisna olduğunda yüklenen belgeyi kaydetmek yerine atın

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ışı bir değer ya da multi-select olmayan hedef istisna fırlatır
      if Pdf.ImportLoadedInterchangeFromFDF(Source, True, False, Status, Info) then
        Pdf.SaveLoadedDocument('order-form-filled.pdf');
    except
      on E: Exception do
        ShowMessage('Import rejected, nothing saved: ' + E.Message);
    end;
  finally
    Source.Free;
    Pdf.Free;
  end;
end;

XFDF callbacklerini mevcut çağıranları kırmadan genişletmek

Alt düzey XFDF birimindeki dizi desteği ayrı bir kayıtta, THPDFXFDFArrayAccesste ve HPDFXFDFExportFields ile HPDFXFDFImportFieldsin yeni aşırı yüklemelerinde yaşar; mevcut THPDFXFDFAccess kaydının sonuna eklenmiş ek alanlarda değil. Sebep ikili uyumluluktur. THPDFXFDFAccessi yerel bir değişken olarak dolduran kod çoğunlukla yalnızca bildiği yuvaları set eder ve gerisini hiç temizlemez; o kayda eklenen yeni bir fonksiyon işaretçisi yığın çöpü içerirdi ve kütüphane onu gerçek bir callback sanırdı. Ayrı bir kayıtla eski çağıranlar eski düzeni ve eski aşırı yüklemeleri korur ve o aşırı yüklemeler içeride hep-nil bir dizi kaydı geçirir. Özgün skaler içe aktarım aşırı yüklemesi uyumluluk için yinelenen değerleri hâlâ LF ile birleştirir ve onları ayıran tek yer dizi-farkında aşırı yüklemedir. Kendi veri deponuzu bağlarken Default(THPDFXFDFArrayAccess)ten başlayın. Liste değerli her alan için — hiçbir şey seçilmemiş olanda dahil — GetFormFieldValueArrayden True döndürün ve skaler callbacke düşmek için False

uses HPDFXFDF;

// Düz fonksiyon işaretçisi, "of object" değil: Context sizin deponuzu taşır
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); // her kullanılmayan yuva nil
  ArrayAccess.GetFormFieldValueArray := StoreGetSelections;
  HPDFXFDFExportFields(Access, ArrayAccess, Bytes);
end;

Multi-select takası, zaten var olan ve /Ffte MultiSelect biti set edilmiş liste kutularında çalışır. Choice alanlarının ve bayrak bitlerinin başta nasıl oluşturulduğu için yüklü bir PDF'e ListBox ve diğer AcroForm alanlarını eklemeke bakın. XFDF'in <annots> ağacından geçen yorum işaretlemesi için HotPDF'de XFDF anotasyon içe ve dışa aktarmaya bakın. Tam API referansı ve deneme indirmesi HotPDF Delphi PDF component sayfasındadır