Teknik Makale

PDFium Component ile Delphi'de PDF Ekleri

PDF dosya ekleri belgenin gömülü dosya ağacında saklanır; bu yapıyı çoğu görüntüleyici bir ataç paneli ya da ekler kenar çubuğu olarak gösterir. Delphi kodundan PDFium Component bu ağacı TPdf üzerindeki küçük bir dizinli özellik kümesiyle sunar: tamsayı dizinle dolaşır, adları ve bayt yüklerini okur, yeni yuvalar oluşturur ve mevcut olanları silersiniz. API yüzeyi dardır; çevresine üretim kodu yazmadan önce bilinmeye değer yalnızca birkaç sıralama kısıtı ve bir temizleme kuralı vardır

Açık bir belgeden ekleri okumak

AttachmentCount, belgenin bildirdiği gömülü dosya sayısını verir. Doğrudan PDFium'un alttaki çağrısından okur; dolayısıyla yalnızca PDF dosyasının gerçekte içerdiğini yansıtır. Oradan AttachmentName[Index] görüntü adını bir WString olarak döndürür ve Attachment[Index] ham baytları bir TBytes dizisi olarak verir. Her ikisi de sıfır tabanlıdır. Her iki özelliği sorgulamadan önce belgenin açık olması gerekir (Pdf.Active = True); onları kapalı bir belgede çağırmak, istisna olmadan sıfır ya da boş bir sonuç verir

Akılda tutulacak bir şey: Attachment[Index] her okumada dosya yükünün tamamını ayırıp döndürür. Büyük bir gömülü varlık taşıyan bir belgede, bir görüntü listesi kurmak için tüm ekleri dolaşmak bu ayırma bedelini her çağrıda ödemek demektir. Görüntüleme amacıyla yalnızca adlara ihtiyacınız varsa önce AttachmentName okuyun ve bayt alımını kullanıcı dosyayı gerçekten isteyene dek erteleyin

Delphi için PDFium Component içinde PDF gömülü dosya ağacının TPdf ek özelliklerine haritası: AttachmentCount, AttachmentName, Attachment baytları ve AttachmentType
Gömülü dosyalar /Names → /EmbeddedFiles ağacında yaşar ve bileşen aynı yuvaları sayı, ad, bayt ve MIME türü için dizinli TPdf özellikleri olarak sunar
procedure ListAttachments(Pdf: TPdf);
var
  I: Integer;
  Data: TBytes;
begin
  if not Pdf.Active then
    Exit;

  for I := 0 to Pdf.AttachmentCount - 1 do
  begin
    Data := Pdf.Attachment[I];
    Writeln(Format('%d: %s (%d bytes)',
      [I, Pdf.AttachmentName[I], Length(Data)]));
  end;
end;

Bir eki diske çıkarmak

Bir SaveAttachment yardımcısı yoktur. Baytları okur ve gerektiği yere yazarsınız; bu da yol kurma ve temizleme işini tümüyle kodunuza bırakır. Ek adları güvenilmeyen belgelerden geldiğinde bu önem taşır. PDF ek adları dosyanın içinde saklanan dizgelerdir; yol ayırıcıları, Unicode benzerleri ve doğrudan TFileStream.Create çağrısına verirseniz beklenmedik sonuçlar üretecek başka karakterler içerebilirler. Herhangi bir çıktı yolu kurmadan önce adı her zaman ExtractFileName içinden geçirin ve noktayla başlayan ya da sisteminizin beklediğinin dışında karakterler içeren adları reddetmeyi düşünün

Attachment[Index] tarafından döndürülen bayt dizisi çağırana aittir. Onu sıradan bir TFileStream ile yazın; bildirilen ada güvenmek yerine gerçek dosya biçimini doğrulamak için ilk birkaç baytı incelemek dahil, onunla istediğinizi yapabilirsiniz

Delphi'de güvenilmeyen bir PDF ek adını çıktı yolunu kurmadan önce ExtractFileName ile temizlemek, sonra PDFium Component ile çıkarırken bildirilen ada güvenmek yerine imza baytlarını doğrulamak
Güvenilmeyen belgelerden gelen adlar ayırıcı ve benzer karakter taşıyabilir; bu yüzden çıkarma, herhangi bir TFileStream oluşturulmadan önce ExtractFileName ile bir yedek addan geçer
procedure ExtractAttachment(Pdf: TPdf; Index: Integer; const OutputDir: string);
var
  SafeName: string;
  OutPath: string;
  Data: TBytes;
  FS: TFileStream;
begin
  SafeName := ExtractFileName(Pdf.AttachmentName[Index]);
  if SafeName = '' then
    SafeName := Format('attachment_%d', [Index]);

  OutPath := IncludeTrailingPathDelimiter(OutputDir) + SafeName;
  Data := Pdf.Attachment[Index];

  FS := TFileStream.Create(OutPath, fmCreate);
  try
    if Length(Data) > 0 then
      FS.WriteBuffer(Data[0], Length(Data));
  finally
    FS.Free;
  end;
end;

Ek eklemek ve iki adımlı yazma

Bir ek oluşturmak tek değil iki çağrı gerektirir. CreateAttachment(Name) gömülü dosya ağacında yeni bir yuva kaydeder ve başarıda True döndürür. O yuva boş başlar. Sonra en son oluşturulan girdiyi hedefleyerek Attachment[AttachmentCount - 1] konumuna yazarak yükü atarsınız. CreateAttachment False döndürürse yuva oluşturulmamıştır ve atama, o an son olan dizindeki eki bozar

Ek listesini değiştirdikten sonra değişiklikler yalnızca bellekte yaşar. Güncellenmiş gömülü dosya ağacıyla yeni bir dosya yazmak için SaveAs çağırın. PDFium Component o an açık olan aynı dosyaya geri kaydetmeyi desteklemez; çünkü motor kaynağa bir okuma tanıtıcısı tutar. Yerinde güncelleme için standart desen, geçici bir yola kaydetmek, belgeyi kapatmak, özgün dosyayı silmek ya da yeniden adlandırmak, sonra geçici dosyayı yerine adlandırıp yeniden açmaktır

Delphi'de PDFium Component ile iki adımlı PDF eki oluşturma: CreateAttachment boş bir yuva ayırır, yük son dizine atanır ve açık dosya yerinde yazılamadığı için SaveAs geçici dosya takasıyla yayımlar
CreateAttachment yalnızca yük ataması için boş bir yuva ayırır ve değişiklik, SaveAs geçici dosya takasıyla yeni bir dosya yazana dek bellekte kalır
procedure AddFileAttachment(Pdf: TPdf; const FilePath: string);
var
  FS: TFileStream;
  Data: TBytes;
  AttachName: string;
begin
  if not Pdf.Active then
    Exit;

  FS := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyWrite);
  try
    SetLength(Data, FS.Size);
    if FS.Size > 0 then
      FS.ReadBuffer(Data[0], FS.Size);
  finally
    FS.Free;
  end;

  AttachName := ExtractFileName(FilePath);
  if Pdf.CreateAttachment(AttachName) then
    Pdf.Attachment[Pdf.AttachmentCount - 1] := Data;
end;

Ek tür bilgisi

Ad ve bayt yükünün ötesinde AttachmentType[Index], dosya ilk eklendiğinde kaydedilmişse PDF'in gömülü dosya sözlüğünde saklanan MIME türü dizgesini döndürür. Birçok üretici bu alanı boş bırakır ya da application/octet-stream gibi genel bir değere ayarlar; dolayısıyla bir üretim hattında biçim tespiti için ona güvenemezsiniz. Güvenilir tanımlama için yükün ilk birkaç baytını okuyup bilinen dosya imzalarını denetleyin: iç içe bir PDF için %PDF, Office Open XML belgeleri için ZIP yerel dosya başlığı PK\x03\x04, eski bileşik dosya ikilileri için \xD0\xCF\x11\xE0. Sözlükten gelen tür bilgisini bir arayüz etiketinde göstermek sorun değildir ama gerçek baytlar elinizdeyken işleme kararlarını sürmemelidir

Ekleri silmek

DeleteAttachment(Index) o konumdaki girdiyi kaldırır ve başarıda True döndürür. Silmeden sonra kalan girdiler aşağı kayar; dolayısıyla bir döngüde birden çok ek siliyorsanız her kaymadan sonra girdi atlamamak için ileriye değil son dizinden geriye doğru ilerlemeniz gerekir. Değişiklik, SaveAs çağırana dek bellektedir

Belge işleme hatlarında yaygın bir senaryo, güvenlik ya da boyut nedeniyle gelen bir PDF dosyasındaki bütün ekleri aşağı akışa vermeden önce sıyırmaktır. Döngüden önce bir kez sayın ve tersten ilerleyin:

procedure StripAllAttachments(Pdf: TPdf);
var
  I: Integer;
begin
  for I := Pdf.AttachmentCount - 1 downto 0 do
    Pdf.DeleteAttachment(I);
end;

PDF ekleri pratikte nerede karşınıza çıkar

Ek API arayüzü PDFium'un açabildiği her PDF üzerinde çalışır ama gömülü dosyalarla gerçekten karşılaştığınız belgeler birkaç belirli durum çevresinde kümelenir. PDF/A-3 (ISO 19005-3), kaynak veriyi arşiv sunumunun yanında paketlemenin bir yolu olarak uyumlu gömülü dosyalara açıkça izin verir; ZUGFeRD ve Factur-X elektronik faturaları, insan tarafından okunabilir PDF düzeninin içine yapılandırılmış bir XML yükü gömmek için tam olarak buna dayanır. E-postadan türetilmiş PDF dosyaları bazen özgün ileti eklerini gömülü dosya ağacına iletilmiş olarak taşır. Yapılandırılmış yazım sistemlerinden gelen teknik belgeler de zaman zaman destekleyici varlıkları aynı biçimde paketler

Uygulamanız kuruluşunuzun dışından gelen PDF dosyalarını işliyorsa, belge girişinin bir parçası olarak AttachmentCount denetlemek iki bağımsız nedenle yapmaya değer. Birincisi, gömülü dosyalar bir fatura PDF dosyasının içindeki XML gibi çıkarıp işlemek isteyeceğiniz veri taşıyabilir. İkincisi, gömülü dosyalar rastgele çalıştırılabilir içerik taşıyabilir; dolayısıyla neyin bulunduğunu bilmek, onu hiç çıkarmayı düşünmeseniz bile önemlidir. Hiçbiri karmaşık bir şey yapmanızı gerektirmez: sayıyı okuyun, adları denetleyin ve baytlarla ne yapacağınıza karar verin

Burada gösterilen ek özellikleri, Delphi ve C++Builder için PDFium Component parçasıdır