Teknik Makale

Delphi'de PDF İçerik Akışlarını Güvenle Yeniden Yazma

HotPDF, bir sayfanın içerik akışındaki her operatörü dolaşır ve her birini VisitLoadedPageContentOperators aracılığıyla kodunuza teslim eder. Her operatör için bir eylem seçersiniz: koruyun, silin, kendi baytlarınızla değiştirin, dolaşmayı durdurun veya tüm düzenlemeyi reddedin. Her operatör korunduğunda, yeniden yazılan akış özgününle bayt bayt özdeştir; bu da mekanizmayı sizin oluşturmadığınız belgeler üzerinde çalıştırmayı güvenli kılan özelliktir

İçerik akışı düzenleme, PDF araçlarının genellikle yanlış gittiği yerdir. Bir akış, bir belge ağacı değil, bir yığın makinesi üzerindeki bir operatör dizisidir ve kod çözülmüş baytlar üzerinde bir düzenli ifade çalıştırmanın naif yaklaşımı, bir dize sabit değeri bir operatöre benzeyen bir şey içerdiği anda veya satır içi bir görüntü rastgele ikili veri içerdiği anda bozulur. Akışı zaten belirteçlere ayırmış bir ziyaretçinin bu sorunu yoktur

BT, Tf ve TJ gibi PDF içerik akışı operatörlerinin, aşama, operatör adı, işlenen sayısı, bayt aralığı ve iç içe geçme durumuyla birlikte ziyaretçi olay verisine aktığını ve ardından beş eylemden birini döndürdüğünü gösteren diyagram: koru, değiştir, sil, durdur veya reddet
Ziyaret edilen her operatör tam olarak bir karar üretir ve her şeyi koruyan bir geçiş, bayt bazında özdeş çıktı için dokunulmamış aralıkları geri yazar

Operatör Düzeyinde Erişimle Gerçekte Ne Yapabilirsiniz?

Üç görev ailesi vardır. Denetleme: bir sayfanın bilinen bir kümenin dışında kaç kez renk ayarladığını saymak veya gömülü olmayan bir yazı tipiyle çizilmiş metni bulmak. Kaldırma: işaretli içerik olarak çizilmiş bir filigranı silmek veya belirli bir görüntü çağrısını ayıklamak. Ve normalleştirme: bir operatör biçimini, sonraki bir tüketicinin daha iyi işlediği eşdeğer bir biçimle değiştirmek

Bunların her biri bir gereksinimi paylaşır: akışın geri kalan yüzde 99'unu dokunulmamış bırakabilmelisiniz. HotPDF'in sözdizimi ağacı, kaynağın bir kopyasını artı kompakt aralıkları saklar ve düzenlemeler sıralanmış, çakışmayan katmanlardır. Sonucu yazmak, dokunulmamış aralıkları doğrudan kopyalar ve yalnızca bir şey değiştirdiğiniz yerlerde değiştirme baytları yayar, bu yüzden hiçbir şey yapmayan bir gidiş-dönüş sapma getiremez

Ziyaretçi Sözleşmesi

Arayüzü uygulayın veya olayı atayın, ardından bir denetim için ApplyChanges'ı false, bir yeniden yazma için true olarak ayarlayarak ziyaret yöntemini çağırın. Olay verisi; aşamayı, operatör adını, işlenen sayısını, bayt aralığını, geçerli iç içe geçme durumunu ve işlenenleri okumak için ödünç alınmış bir görünümü taşır:

uses
  HPDFDoc, HPDFContentStream;

procedure TAuditor.HandleOperator(Sender: TObject;
  var EventData: THPDFContentOperatorEventData);
var
  Bytes: TBytes;
begin
  if EventData.Phase <> covpOperator then
    Exit;

  // İşaretli içerik içinde çizilen metin gösteren operatörleri say
  if (EventData.OperatorName = 'Tj') or
     (EventData.OperatorName = 'TJ') then
  begin
    Inc(FTextOperators);
    if EventData.State.MarkedContentDepth > 0 then
      Inc(FTextInsideMarkedContent);
  end;

  // Bir işleneni yalnızca ihtiyacınız olduğunda okuyun
  if EventData.OperatorName = 'Do' then
    if EventData.View.GetOperandBytes(0, Bytes) then
      NoteXObjectUse(TEncoding.ANSI.GetString(Bytes));

  EventData.Action := coaKeep;
end;

procedure TAuditor.Run(const FileName: string);
var
  Pdf: THotPDF;
  Stats: THPDFContentOperatorVisitStatistics;
  I: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile(FileName) <= 0 then
      Exit;
    Pdf.OnContentOperator := HandleOperator;
    for I := 0 to Pdf.LoadedPageCount - 1 do
      Pdf.VisitLoadedPageContentOperators(I, False, Stats);   // yalnızca denetim
    Writeln(Format('%d operators visited, max q/Q depth %d',
      [Stats.VisitedOperatorCount, Stats.MaximumGraphicsStateDepth]));
  finally
    Pdf.Free;
  end;
end;

Görünüm Neden Değiştirilebilir Değil de Ödünç Alınmıştır?

Geri çağrı hiçbir zaman değiştirilebilir bir sözdizimi ağacı almaz. Talep üzerine geçerli işlemin baytlarını veya belirli bir işleneni getirebilen bir görünüm alır, hepsi bu kadardır. Bunun yerine her değişiklik, eylem ve değiştirme alanlarından geçer

Bu dolaylılık bir formalite değildir. Doğrudan ağaç değişikliği, bir ziyaretçinin değiştirme bütçesini ve çakışan düzenleme kontrolünü atlamasına izin verirdi ve bir bayt aralığı katman modelinde çakışan düzenlemeler, gürültülü şekilde bozuk olmak yerine sessizce yanlış olan bir çıktı üretir. Değişiklikleri eylem alanı üzerinden yönlendirmek, ziyaretçinin ne amaçladığından bağımsız olarak her düzenlemenin aynı şekilde doğrulandığı anlamına gelir

Talep üzerine okuma, yaygın durumu da ucuz tutar. Yalnızca operatör adlarını sayan bir denetim, işlenen baytlarını hiçbir zaman somutlaştırmaz; bu, yüz binlerce işlem içeren bir içerik akışında önemlidir

Durum Alanı Her Zaman Girişi Tanımlar

Her olay; grafik durumu derinliğini, metin nesnesi derinliğini, işaretli içerik derinliğini ve uyumluluk bölümü derinliğini, o operatör çalıştırılmadan hemen önce özgün girişte oldukları haliyle taşır. Ziyaretçiniz önceki operatörleri silmiş veya değiştirmiş olsa bile bu geçerlidir

Durumu düzenlenmiş çıktı yerine özgün girişe bağlamak, pratik bir getirisi olan kasıtlı bir seçimdir: aynı denetimin aynı belge üzerinde iki kez çalıştırılması özdeş sonuçlar üretir ve düzenlemelerle birlikte çalışan bir denetim kendi referans çerçevesini kaydırmaz. Çıktıdaki derinliği bilmeniz gerekiyorsa neyi sildiğinizi bilirsiniz, bu yüzden onu hesaplayabilirsiniz; tersi, yani hareketli bir çıktıdan kararlı giriş konumlarını yeniden inşa etmek mümkün değildir

Sizi Koruyan Kurallarla Silme ve Değiştirme

Bir değiştirme, ham içerik akışı baytlarıdır ve sözdizimlerinden siz sorumlusunuz. Bunların etrafındaki korumalar; toplam değiştirme baytları üzerindeki bütçe, çakışmama gerekliliği ve kabul edilmeden önce sonucu yeniden ayrıştıran isteğe bağlı çıktı doğrulamasıdır:

Delphi'de yeniden yazma korumaları: Action ve Replacement niyetini bildiren bir ziyaretçinin, değiştirme bütçesi, çakışmayan bayt aralığı ve çıktı doğrulama korumaları üzerinden doğrulanmış kaydedilmiş sayfaya yönlendirilmesi
Her değişiklik, eylem alanı üzerinden bütçe, çakışma ve yeniden ayrıştırma korumalarına akar, bu yüzden başarısız doğrulama, bozuk operatörler yazmak yerine iptal eder
procedure TWatermarkStripper.HandleOperator(Sender: TObject;
  var EventData: THPDFContentOperatorEventData);
begin
  EventData.Action := coaKeep;
  if EventData.Phase <> covpOperator then
    Exit;

  // Filigran formunu boyayan operatörleri düşür
  if (EventData.OperatorName = 'Do') and OperandIsWatermark(EventData) then
    EventData.Action := coaDelete
  // Başıboş bir rengi kurumsal griye normalize et
  else if EventData.OperatorName = 'rg' then
  begin
    EventData.Replacement := TEncoding.ANSI.GetBytes('0.2 0.2 0.2 rg');
    EventData.Action := coaReplace;
  end
  // Anlamadığımız bir şeyle karşılaşırsak tüm düzenlemeyi reddet
  else if EventData.OperatorName = 'BI' then
    EventData.Action := coaReject;
end;

var
  Options: THPDFContentOperatorVisitOptions;
begin
  Options := THPDFContentOperatorVisitOptions.Default;
  Options.ValidateOutput := True;        // kabul etmeden önce yeniden ayrıştır
  Options.MaxReplacementBytes := 64 * 1024;

  Pdf.OnContentOperator := HandleOperator;
  if Pdf.VisitLoadedPageContentOperators(PageIndex, True, Options, nil, Stats) then
    if Stats.Modified then
      Pdf.SaveLoadedDocument('cleaned.pdf');
end;

Eylem kümesi bir nedenden ötürü iki çıkış içerir. coaStop, dolaşmayı sonlandırır ve o ana kadar karar verilenleri korur; bu, aradığınızı bulduğunuzda doğrudur. coaReject, tüm ziyareti terk eder ve sayfayı dokunulmamış bırakır; bu, yeniden yazma kurallarınızın kapsamadığı bir yapıyla karşılaştığınızda doğrudur. Satır içi görüntüler klasik bir örnektir: ikili yükleri, bir renk normalleştiren ziyaretçinin etrafında düzenleme yapması gereken bir şey değildir

İstatistikleri Okumak ve Dolaşmanın Durduğu Yer

Durum alanı; tamamlanmayı durdurmadan, reddetmeden, geçersiz girişten, aşılan bir sınırdan, geçersiz çıktıdan ve bir ziyaretçi hatasından ayırır. Bunlar farklı sorunlardır: geçersiz çıktı, değiştirme baytlarınızın yeniden ayrıştırılamadığı anlamına gelirken geçersiz giriş, kaynak akışın zaten bozuk olduğu ve hiçbir düzenlemenin denenmediği anlamına gelir

Sayaçlar, bir denetimi kendi kendini kontrol eden hale getirir. VisitedOperatorCount, KeptOperatorCount, DeletedOperatorCount ve ReplacedOperatorCount ile toplanmalıdır ve maksimum derinlik rakamları, sayfanın daha yakından bakmaya değecek kadar derin iç içe geçip geçmediğini söyler. SourceBytes'ın OutputBytes ile karşılaştırılması, düzenlemenizin boyut etkisini tek bir sayıda verir

Korunan, silinen ve değiştirilen operatör sayılarının ziyaret edilen sayıya toplandığı denetim istatistikleri uzlaştırmasını, tamamlanandan ziyaretçi hatasına kadar yedi durum sonucunun yanında ve durum derinliklerinin özgün girişi tanımladığı için denetimlerin özdeş şekilde tekrarlandığı notuyla birlikte gösteren diyagram
Toplanması gereken sayaçlar ile mümkün olan her sonucu adlandıran bir durum, sizin oluşturmadığınız belgeler üzerinde bir denetimi kendi kendini kontrol eden hale getirir

Yaygın görevler için bir ziyaretçi yazmayı hiç gerektirmeyen daha üst düzey yollar vardır. İçeriği kalıcı olarak kaldırmak, gerçek redaksiyonun ihtiyaç duyduğu kaynak temizliğiyle birlikte, yüklenmiş belgelerde redaksiyon ve sayfa dikişi başlığında ele alınmıştır. Görünür metni değiştirmek, mevcut bir PDF'de arama ve değiştirme başlığında ele alınmıştır. Kuralınız metinsel değil yapısal olduğunda veya hiçbir mevcut raporun yanıtlamadığı bir denetime ihtiyaç duyduğunuzda operatör ziyaretçisine başvurun

İçerik akışı ziyareti, çıkarma ve yeniden yazma, Delphi ve C++Builder'da aynı yüklenmiş belge modeli üzerinde çalışır; eksiksiz özellik listesi HotPDF Delphi PDF bileşeni sayfasında bulunur