Teknik Makale

Delphi'de PDFium Component CLI ile Toplu PDF Preflight Raporları

Toplu preflight aracı, penceresi olmayan bir konsol programıdır: bir PDF klasörüne yönlendirilir, her dosyayı adlandırdığınız uyumluluk standartlarına göre doğrular ve bulunanların makine tarafından okunabilir kanıtını geride bırakır. Kimse onu izleyerek beklemez. Gece saat ikide cron ya da Windows Görev Zamanlayıcısı altında veya bir CI ardışık düzeninde geçit noktası olarak çalışır; çıktısına önem veren bir sonraki kişi, bir çıkış kodunu okuyan zamanlayıcı ya da haftalar sonra rapor açan bir denetçidir. Bu durum "doğru"nun ne anlama geldiğini değiştirir. Delphi, C++Builder ve Lazarus için kaynak kodlu bir PDF kütüphanesi olan PDFium Component'ın preflight motoru, doğrulama çağrılarını neredeyse önemsiz kılar. Aracın faydalı olup olmadığına karar veren asıl çalışma bu çağrıların çevresindedir: hangi profili kontrol ettiğiniz, çıkış kodunun zamanlayıcıya ne söylediği ve bir hatayı yakalayacak raporun biri onu aradığında hâlâ mevcut olup olmadığı

Sözleşme: bir zamanlayıcı gerçekte ne görebilir

Bir CI çalıştırıcısı veya Windows Görev Zamanlayıcısı aracınızdan yalnızca iki şeyi görür: çıkış kodunu ve geride bıraktığı dosyaları. Günlük satırları, konsol renkleri, ilerleme çıktısı – bunların hepsi canlı izleyen bir insan içindir ve gece saat ikide kimse orada değildir. Bu nedenle API'ye dokunmadan önce çıkış kodu sözlüğünü düzeltin ve sıradan tutun:

  • 0: her dosya istenen her profile uygundur
  • 1: en az bir dosya doğrulama bulgusu üretmiştir
  • 2: araç en az bir dosyada başarısız olmuştur (bozuk girdi, kilit, çökme)

1 ve 2 kodları arasındaki fark, ekiplerin atladığı ve sonradan pişman olduğu farktır. Açılamayan bozuk bir PDF, doğrulama başarısızlığı değildir. Bunu kod 1'e dahil edin; çöp haline gelmiş taramaların yükü gösterge tablolarınızda ani bir uyumluluk çöküşü olarak görünür, birini hiç yaşanmamış bir standartlar gerileme kovalarken aslında asıl sorun yukarı akıştaki bozuk bir tarayıcıdır

Sözleşmeye iki madde daha aittir. Birincisi, dosya başına zaman aşımı. Binlerce sayfalı ve derin iç içe nesne yapılarına sahip patolojik bir PDF, tek bir doğrulama geçişini dakikalarca tutabilir; gecelik bir pencere bunun için sabırsızdır. O dosyanın işini son tarihe kadar sonlandırın, bir araç başarısızlığı olarak sayın ve toplu işlemi sürdürün. İkincisi, karantina dizini: zaman aşımına uğramış veya açılamayan her girdiyi bırakmak yerine kenara taşıyın. Birkaç ay içinde bu dizin, gerçek müşterilerinizin gönderdiği en kötü belgeleri sessizce biriktirir; bu külliyat, elle yazabileceğiniz herhangi bir sentetik örnekten sürüm testine daha değerlidir

Standartlar seçimi ve uyumluluk düzeyinin önemi

TPdfPreflightStandard numaralandırması pratikte karşılaşılan aileleri kapsar: ISO 19005 arşiv uyumu için ppsPdfA, ISO 14289 erişilebilirliği için ppsPdfUa, baskı değişimi için ppsPdfX; ayrıca mühendislik, raster ve değişken veri çalışmaları için ppsPdfE, ppsPdfR ve ppsPdfVT. Motor, bir aile içinde belgenin iddia ettiği uyumluluk düzeyini okur ve sonucun ConformanceName alanında standart başına raporlar. Aileyi adlandırmak nadiren yeterlidir, çünkü gerçek fark düzeyde yaşar. PDF/A-2b yalnızca görsel yeniden üretmeyi vaat eder, başka bir şey değil. PDF/A-3a mantıksal yapı etiketlemesi talebi ekler ve gömülü kaynak dosyalara izin verir; bu, etiket ağacı hiç olmayan taranmış materyaller için çok daha zorlu bir çıtadır. Bunu her iki yönde yanlış anlarsanız toplu işlem size yalan söyler. Saklama politikanız gerçekte PDF/A-2b istiyorsa ancak eksik yapı etiketleri için dosyaları reddediyorsanız, rapor kimsenin düzeltmeyeceği bulgularla dolar. Düzeyi kontrol etmeden herhangi bir PDF/A etiketini kabul ederseniz, vaat ettiğinizden daha zayıf bir çıtayı karşılayan belgeleri onaylarsınız. Hükümet alıcılarının erişilebilirlik gereksinimleri PDF/UA'yı tüm bunların üzerine giderek daha fazla ekliyor; bu, çalıştırmaya ek bir maliyet getirmez, çünkü BuildPdfPreflightReport (FPdfPreflightReport biriminden) bir standartlar kümesi alır:

Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);

Tek çağrı her iki standardı değerlendirir ve tek bir birleştirilmiş rapor kaydı döndürür

Boş bulgular listesi neden geçer sayılmaz

Rapor, standart başına bulguları listeler ve boş bir sorun listesi yalnızca şunu ifade eder: "gerçekten çalışan standartlarda sorun bulunamadı." Bu, "önem verdiğiniz standarda uygun" iddiasından daha dar bir iddiadır; ikisi arasındaki boşluk, toplu preflight'ın sessizce çürüdüğü yerdir. ppsPdfA'yı kümeden düşüren bir yapılandırma yazım hatası, gerçekten temiz bir dosyayla tamamen aynı boş sorun listesini üretir. Bu nedenle sessizliği şüpheli sayın. Report.Results üzerinde dolaşın ve kontrol etmek istediğiniz her standart için iki şeyi doğrulayın: o standart için bir sonuç girişinin gerçekten mevcut olduğunu ve Status = pfsPass tarafından desteklenen IsCompliant bayrağının doğru olduğunu. Hangi standartların değerlendirildiğini hiçbir zaman doğrulamadan "bulgu yok" ile "arşive hazır"ı eşitleyen gecelik bir iş, uyumsuz dosyaların aylarca oradan geçmesinin klasik yoludur; ta ki bir dış denetçi birini veraPDF ile açana ve tüm arşiv sorgulanana kadar

İkinci bir tuzak, bulgunun ne olduğunda gizlenir. Her TPdfPreflightIssue, bir Code, bir Category, bir Description ve bir Recommendation taşır; ihlal edilen kuralı adlandırır, sayfa veya nesneyi değil. Bu, geri bildirim döngüsü için sonuçları olan bilinçli bir tasarım seçimidir. Rapor, üretim ekibine hangi kusur sınıfının mevcut olduğunu söyler – gömülmemiş bir yazı tipi veya eksik bir XMP tanımlayıcısı – ve belirli sorunlu nesneyi bulmak, doğrulayıcının değil, aşağı akıştaki düzeltme aracının işidir. Rapor tüketicilerinizi, uyarılar arasında yeniden ifade edilebilen insan tarafından okunabilir açıklama metnine değil, kararlı Code değerlerine göre oluşturun

Makineler ve nöbetçi için rapor dosyaları

Rapor kaydı aynı bulguları beş formatta yazar: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile ve SaveMarkdownToFile; her birinin dizeyi diske değil bellekte istediğinizde ToJson tarzı bir işlev karşılığı vardır. Birini seçme isteğine direniyor, ardışık düzen için JSON yazın; CI bunu iş kaydına ekleyebilir ve metin kazımak zorunda kalmadan sorun kodlarını ve standart başına durumları ayrıştırabilir. Çağrılan insan için HTML yazın, çünkü herhangi bir tarayıcıda hiçbir araç kullanmadan açılır. İkisi birlikte dosya başına fazladan bir satıra mal olur ve nöbetçi mühendisini toplu işlemedeki en kötü görevden kurtarır: gece saat ikide hangi dosyanın bozulduğunu öğrenmek için ham JSON bloğunun tersine mühendisliğini yapmak. Biçim seçiminden daha önemli olan bir disiplin: her rapor adını zaman damgasından değil, girdi dosyası adından türetin; aksi takdirde iki paralel çalıştırma, artık girişlerine eşleştiremeyeceğiniz raporları birbirine karıştırır

Önem düzeyi eşikleri koda değil, yapılandırmaya aittir. Alternatif açıklaması olmayan bir ek açıklama, PDF/UA gönderim portalı için kesin bir başarısızlıktır; dahili bir arşiv için ise görmezden gelinebilir bir not, ancak her iki durumda da aynı bulgudur. Profil başına bir başarısız olma düzeyi sunun, böylece politika yeniden derlemeden değiştirilebilir; o an yürürlükte olan düzeyi iş özeti içine damgalayın. Bir sonraki çeyreğe gelindiğinde kimse geçen Ekim'in toplu işleminin hangi eşikle çalıştığını hatırlamaz; özet, o belleğin hayatta kaldığı tek yerdir

Dosyaları yalıtmak, böylece tek bir kötü PDF toplu işlemi batıramaz

procedure RunPreflightBatch(const InputDir, ReportDir: string;
  out FilesWithFindings, ToolFailures: Integer);
var
  SR: TSearchRec;
  Pdf: TPdf;
  Report: TPdfPreflightReport;
begin
  FilesWithFindings := 0;
  ToolFailures := 0;
  if FindFirst(InputDir + '*.pdf', faAnyFile, SR) = 0 then
  try
    repeat
      Pdf := TPdf.Create(nil);   // fresh instance per file: no state bleed
      try
        try
          Pdf.FileName := InputDir + SR.Name;
          Pdf.Active := True;
          if not Pdf.Active then  // load failures are silent, not raised
            raise EPdfError.Create('Cannot open ' + SR.Name);
          Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
          Report.SaveJsonToFile(ReportDir + ChangeFileExt(SR.Name, '.json'));
          Report.SaveHtmlToFile(ReportDir + ChangeFileExt(SR.Name, '.html'));
          if Report.TotalIssueCount > 0 then
            Inc(FilesWithFindings);
        except
          on E: Exception do
          begin
            Inc(ToolFailures);   // exit-code-2 territory, not a validation verdict
            WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
          end;
        end;
      finally
        Pdf.Free;
      end;
    until FindNext(SR) <> 0;
  finally
    FindClose(SR);
  end;
end;

Bu döngüde üç bilinçli seçim yaşar. Dosya başına yeni bir TPdf, motor durumunu bozan tek bir belgenin ardından gelen dosyaları zehirleyememesini garanti eder. Açık Active kontrolü yerini hak eder, çünkü Active := True yükleme hatalarını yükseltmek yerine yutar; bu koruyu kaldırırsanız, kısaltılmış bir dosya doğrulama çağrısına sürüklenir ve yanıltıcı bir mesajla aşağı akışta bir yerde başarısız olur. İçteki try..except, dosya başına kapsamın içinde kasıtlı olarak yaşar; bu sayede tek bir istisna başarısızlık sayacını artırır ve döngü devam eder. 4.999 iyi dosya için temiz raporlar istiyorsunuz, 5.000. dosya parçalanmış olsa bile. Ve her iki rapor formatı da sonuç sayılmadan önce diske yazılır; bu da, özet mantığındaki bir hata sonradan yanlış saysa bile kanıtın hayatta kalacağı anlamına gelir

Çıkış kodu eşlemesi daha sonra proje dosyasında birkaç satıra indirgenir:

begin
  RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
  if Failures > 0 then
    Halt(2)
  else if Findings > 0 then
    Halt(1);
  // falling through exits with 0: every file conformed
end.

Preflight'ın sizin için yapamayacağı şeyler

Motor tespit eder; onarmaz. Gömülmemiş bir yazı tipi veya cihaza bağlı renk uzayı hakkındaki bir bulgu, dosyaları üreten kişi için bir iş emridir; doğrulayıcının bunu yerinde yamalamak için hiçbir yolu yoktur. Bu nedenle geri bildirim döngüsünü bilinçli olarak planlayın. Raporların üretim ekibinin gerçekten okuduğu yere ulaşması gerekir, yoksa aynı bulgular her gece yeniden görünür; ta ki biri uyumluluk oranının neden hiç iyileşmediğini sorana kadar. Ayrıca, dış bir denetçi sizin için çapraz kontrol yapmadan önce bağımsız bir doğrulayıcıyla – PDF/A için veraPDF veya PDF/X için Acrobat'ın preflight'ı – bir örnek sonuçları çapraz kontrol etmek de faydalıdır. İki motor gerçek bir müşteri dosyasında anlaşamadığında, bu belge bir baş belası değildir; sürüm testinizin tam olarak kaçırdığı regresyon vakasıdır. Saklayın, adlandırın ve her derlemede çalıştırın

Bir eşleştirme daha bilmeye değer. Aynı doğrulama motoru, bir inceleme kullanıcı arayüzündeki etkileşimli kontrolleri çalıştırır; bu nedenle bu başsız CLI ve analist odaklı bir PDF giriş inceleme tezgahı, zaman içinde birbirinden uzaklaşmak yerine tek bir doğrulama sözlüğünü paylaşabilir. Ve [ppsPdfA, ppsPdfUa] erişilebilirliği aynı geçişte değerlendirdiğinden, toplu işlemin PDF/UA tarafı, Delphi'de erişilebilir bir PDF okuyucu oluşturma gibi görüntüleyici taraflı çalışmalarla temiz bir şekilde örtüşür. Profiller, rapor formatları ve tam preflight API'si, PDFium Component ürün sayfasında belgelenmiştir