Teknik Makale

Delphi'de PDFium Bileşeni ile PDF/X Doğrulaması

Delphi için PDFium Bileşeni, baskıya hazır PDF/X belgelerini iki katmanda ISO 15930 kontrollerini uygulayan TPdf.ValidatePdfX aracılığıyla doğrular: sekiz bayt düzeyinde içerik kontrolü (yasaklanmış LZW sıkıştırması, JavaScript, form alanları, OPI referansları, eksik bir TrimBox, ayarlanmamış bir Trapped anahtarı ve daha fazlası) artı her sayfanın her metin nesnesinde yazı tipi gömmeyi doğrulamak için FPDFFont_GetIsEmbedded kullanan bir PDFium nesne modeli geçişi. Sonuç, algılanan uygunluk düzeyini adlandıran ve her ihlali tiplendirilmiş bir enum olarak listeleyen bir TPdfXValidationResult kaydıdır; böylece Delphi uygulamanız bir müşteriye, plak basılmadan önce bir dosyanın baskı merkezinde neden reddedileceğini tam olarak söyleyebilir

Ticari bir matbaaya bir iş gönderdiyseniz ve tek satırlık bir reddetme aldınızsa —"TrimBox yok", "yazı tipleri gömülü değil", "Trapped ayarlanmamış"— bunu geç öğrenmenin maliyetini bilirsiniz. PDF/X, baskı öncesi hazırlık karşılığıdir: arşivlik PDF/A bir belgenin onlarca yıl sonra da aynı şekilde oluşturulacağını garanti ederken, PDF/X bir belgenin yarın sabah bir başkasının RIP cihazında aynı şekilde ayrılacağını, görüntüleneceğini ve kesileceğini garanti eder. İki standart aynı mekanizmaları paylaşır (XMP tanımlama, OutputIntents, gömülü ICC profilleri) ancak farklı soruları yanıtlar, bu nedenle bileşen her biri için ayrı doğrulayıcılar sunar —PDF/A tarafı PDFium Bileşeni ile PDF/A uçuş öncesi (preflight) doğrulaması makalesinde ele alınmıştır

ISO 15930 baskıya hazır bir PDF'ten gerçekte ne talep eder?

ISO 15930, körlemesine değişimi (blind exchange) mümkün kılmak için vardır: bir tasarımcı hiç konuşmadığı bir matbaaya bir dosya teslim eder ve matbaa telefon görüşmesi yapmadan, eksik yazı tipi e-postası almadan ve tasarımcının dizüstü bilgisayarında kalan bağlantılı bir resim olmadan doğru çıktıyı üretebilir. Standarttaki her kural bu amaca hizmet eder. Yazı tipleri gömülü olmalıdır çünkü alıcı RIP cihazının bunlara sahip olduğu varsayılamaz. Harici referanslar yasaktır çünkü dosya kendi içinde eksiksiz olmalıdır. Etkileşimli özellikler yasaktır çünkü mürekkebin onclick işleyicisi yoktur

PDFium Bileşeni üç uygunluk ailesini tanır ve bunları doğrulama sonucundaki TPdfXConformance enum aracılığıyla raporlar: pxc1a, PDF/X-1a:2001 için (ISO 15930-1, PDF 1.3/1.4 üzerinde katı CMYK-artı-spot temeli); pxc3, PDF/X-3:2002 için (RGB, Lab ve ICC yönetimli renkleri kabul eder); ve pxc4, PDF/X-4:2010 için (PDF 1.6 tabanında canlı şeffaflığa ve katmanlara izin verir). Hiçbir PDF/X tanımlaması taşımayan bir dosya pxcNone olarak geri döner, bu da kendi başına yararlı bir cevaptır: belge hiçbir zaman baskıya hazır olduğunu iddia etmemiştir ve doğrulayıcının rapor ettiği diğer her şey oraya ulaşmak için ne gerektiğini açıklar

Yasaklar, bir RIP satıcısı gibi düşündüğünüzde mantıklıdır. /LZWDecode, uyumlu bir tüketicinin hiçbir zaman uyumluluk ve lisans geçmişi olan bir filtreye bağımlı kalmaması için tüm PDF/X varyantlarında yasaklanmıştır; Flate aynı işi bu bagaj olmadan yapar. JavaScript, AcroForm alanları ve /AA ek eylem (additional-action) sözlükleri yasaktır çünkü bir baskı dosyası kağıt üzerindeki işaretlerin sabit bir açıklaması olmalıdır —açılış anında görünümü değiştirebilecek her şey, prova edilen şeyin basılan şey olduğu garantisini bozar. OPI (Open Prepress Interface) yer tutucuları yasaktır çünkü bunlar tasarım gereği başka bir yerde saklanan yüksek çözünürlüklü görüntülere yapılan referanslardır ve "başka bir yer", körlemesine değişimin tam olarak yasakladığı şeydir

Matbaalar TrimBox olmayan PDF'leri neden reddeder?

TrimBox bitmiş sayfadır —giyotin kesimlerinden sonra kalan dikdörtgen. Her PDF sayfasının sahip olduğu MediaBox ise yalnızca tabakadır: taşma payını (bleed), kesim işaretlerini, hizalama hedeflerini ve renk çubuklarını içerir. Montaj (imposition) yazılımı, sayfaları bir baskı tabakası üzerine TrimBox'larına göre konumlandırır; bu olmadan operatör kartvizitinizin gerçekte nerede bittiğini tahmin etmek zorunda kalır ve yanlış bir tahmin taşma payınızı keser veya bir kenarda beyaz bir şerit bırakır. Bu yüzden ISO 15930 her sayfada bir TrimBox (veya ArtBox) gerektirir ve belgenin herhangi bir sayfasında /TrimBox anahtarı bulunmadığında ValidatePdfX işlevi pvxiMissingTrimBox hatasını fırlatır

/Trapped anahtarı farklı bir üretim sorusunu yanıtlar. Trapping (tuzaklama), baskıdaki küçük hizalama hatalarının renkler arasında beyaz boşluklar açmaması için bitişik renklerin hafifçe üst üste bindirilmesi tekniğidir. Matbaacının bu işin zaten yapılıp yapılmadığını bilmesi gerekir: önceden tuzaklanmış bir dosyayı tekrar tuzaklamak bindirmeleri ikiye katlar, tuzaklanmamış bir dosyada tuzaklamayı atlamak ise görünür boşluklar riski taşır. PDF/X bu nedenle Info sözlüğünün açıkça /Trapped /True veya /Trapped /False belirtmesini gerektirir —eksik bir anahtar veya /Unknown değeri, bir insanın dosyayı incelemesini zorunlu kılar ki bu da körlemesine değişimin ortadan kaldırmayı amaçladığı görüşmedir. Bileşen bunu pvxiTrappedNotSet olarak bayraklar

TPdf.ValidatePdfX ile iki katmanlı doğrulamayı çalıştırma

TPdf.ValidatePdfX hiçbir argüman almaz ve üç üyeli bir TPdfXValidationResult kaydı döndürür: Conformance (algılanan PDF/X türü), Issues (TPdfXValidationIssue değerlerinden oluşan bir Pascal kümesi) ve bir IsCompliant yardımcısı. Dahili olarak, yüklenen belgeyi bir bellek akışına serileştirir, bayt düzeyinde denetleyiciyi bunun üzerinde çalıştırır ve ardından yazı tipi başına gömme kontrolü için PDFium nesne modelini yürütür. Minimal bir uçuş öncesi (preflight) kapısı şuna benzer:

uses PDFium, FPdfPdfx;

procedure CheckPrintReadiness(const FileName: string);
var
  Pdf: TPdf;
  Res: TPdfXValidationResult;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Active := True;

    Res := Pdf.ValidatePdfX;
    Writeln('Detected conformance: ',
      Ord(Res.Conformance)); // pxc1a, pxc3, pxc4, pxcNone...

    if Res.IsCompliant then
      Writeln('PDF/X checks passed')
    else
    begin
      if pvxiMissingTrimBox in Res.Issues then
        Writeln('REJECT: no /TrimBox on the pages');
      if pvxiTrappedNotSet in Res.Issues then
        Writeln('REJECT: /Trapped missing or /Unknown');
      if pvxiPdfiumFontNotEmbedded in Res.Issues then
        Writeln('REJECT: a page uses a non-embedded font');
      if pvxiLzwForbidden in Res.Issues then
        Writeln('REJECT: LZWDecode filter present');
    end;
  finally
    Pdf.Free;
  end;
end;

Issues sıradan bir Pascal kümesi olduğundan, iş akışınızın ihtiyaç duyduğu şekilde bölebilirsiniz —yapısal sorunları kesin reddetmeler olarak ele alın, pvxiMissingTitle (standartta bir MUST değil, SHOULD) uyarısını bir ikaz olarak değerlendirin ve geri kalanını günlüğe kaydedin. Aynı kayıt türü bileşenin rapor oluşturucusunu da besler; dolayısıyla enumlar üzerinde dallanmak yerine insan tarafından okunabilir bir belge üretmeyi tercih ederseniz, PDFium Bileşeni ile toplu uçuş öncesi raporu CLI'si oluşturma makalesindeki desen PDF/X için aynen geçerlidir

Bayt düzeyindeki katmanın yakaladıkları — ve kaçırdıkları

Bayt düzeyindeki katman, akış gövdeleri boşaltılmış olarak belgenin yapısal baytları üzerinde yapılan bir belirteç taramasıdır, bu nedenle rastgele /JavaScript bayt deseni içeren bir JPEG hatalı bir pozitif (false positive) tetikleyemez. İşaretçi kontrollerine ek olarak (XMP pdfxid:GTS_PDFXVersion, gömülü bir ICC profili içeren OutputIntent, trailer /ID, şifreleme yasağı), içerik geçişi her biri kendi enum değerine sahip sekiz kontrol ekler:

  • pvxiLzwForbidden —dosyanın herhangi bir yerinde bir /LZWDecode filtresi görünüyor (tüm PDF/X varyantlarında yasaktır)
  • pvxiJavaScriptForbidden —bir /JavaScript eylemi veya ad ağacı mevcut
  • pvxiFormFieldsForbidden —bir /AcroForm sözlüğü veya /XFA girdisi mevcut
  • pvxiAdditionalActions —bir /AA ek eylemler sözlüğü mevcut
  • pvxiEmbeddedFilesForbidden/EmbeddedFiles veya bir /FileAttachment açıklaması mevcut
  • pvxiOpiForbidden —bir /OPI veya /Alternates girdisi değiştirilebilir görüntü içeriğine başvuruyor
  • pvxiMissingTrimBox —hiçbir sayfada /TrimBox bulunamadı
  • pvxiTrappedNotSet/Trapped mevcut değil veya /Unknown olarak ayarlanmış

Bayt tarama hızlıdır ve oluşturma motoruna ihtiyaç duymaz ancak yazı tipleri konusunda doğal bir kör noktası vardır: bu düzeyde denetleyici yalnızca kaba bir sezgisel yöntem uygulayabilir —hiçbir gömülü yazı tipi programı bulamadığında belgeyi bayraklar. Dokuz yazı tipinin gömülü olduğu ve bir sistem yazı tipinin sızdığı bir dosya, bayt taramasına iyi görünür. Bu tek boşluk, ikinci katmanın var olma nedenidir

PDFium nesne modeli aracılığıyla yazı tipi başına gömme kontrolü

PDFium Bileşeni'nin nesne modeli katmanı yazı tipi sorusunu kesin olarak yanıtlar. Bayt düzeyindeki geçişten sonra TPdf.ValidatePdfX her sayfayı yineler, nesne listesi için FPDFPage_CountObjects işlevini çağırır ve her metin nesnesi için yazı tipi tanıtıcısını FPDFTextObj_GetFont aracılığıyla çözer ve FPDFFont_GetIsEmbedded işlevini sorgular. Belgenin herhangi bir yerindeki tek bir gömülmemiş yazı tipi, sorun kümesine pvxiPdfiumFontNotEmbedded ekler. Arama iki düzeyde kısa devre yapar —bir sayfadaki nesneleri taramayı durdurur ve sorun onaylandığı anda diğer sayfaları yüklemeyi bırakır— bu nedenle ihlalde olan 300 sayfalık bir katalogda karar genellikle birinci sayfadan sonra gelir

Bilinmesi gereken iki sınır notu. İlk olarak, bu katman PDFium kitaplığının yüklenmesini gerektirir ve FPDFFont_GetIsEmbedded işlevini ihraç eden derlemeler gerektirir; ihraç eksik olduğunda kontrol başarısız olmak yerine atlanır, bu nedenle daha eski bir DLL asla hayalet reddetmeler üretmez. İkinci olarak, kontrol "gömülü mü değil mi" sorusunu yanıtlar ve daha fazlasını yapmaz —tam gömme ile alt kümelemeyi (subsetting) ayırmaz veya glif kapsamını denetlemez. Bir dosya başarısız olduğunda ve hangi sayfada hangi yazı tipinin olduğunu bilmeniz gerektiğinde, Delphi'de PDFium ile PDF yazı tipi özelliklerini analiz etme kılavuzundaki listeleme teknikleri, doğrulayıcının boolean değerinin bıraktığı yerden tam olarak devralır

Belgeyi veya DLL'i yüklemeden akışları doğrulama

Bayt düzeyindeki denetleyici, FPdfPdfx birimi içindeki ValidatePdfXCompliance(Source: TStream) bağımsız işlevi olarak da sunulur ve PDFium DLL'ine bağımlılığı olmayan saf Object Pascal'dır. Bu, bir oluşturma motorunun hoş karşılanmadığı yerlerde konuşlandırılabilir olmasını sağlar: bir web sunucusunda hafif bir yükleme kapısı, oluşturulan çizimleri denetleyen bir CI işi veya yerel ikili dosyalar göndermek istemediğiniz bir platformdaki Lazarus hizmeti. Ona herhangi bir aranabilir (seekable) akış besleyin:

uses Classes, FPdfPdfx;

function QuickPdfXGate(const FileName: string): Boolean;
var
  Fs: TFileStream;
  Res: TPdfXValidationResult;
begin
  Fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfXCompliance(Fs);
    Result := Res.IsCompliant and (Res.Conformance <> pxcNone);
  finally
    Fs.Free;
  end;
end;

Ödünleşim (trade-off) açıktır: bağımsız yol işaretçi kontrollerini ve sekiz içerik kontrolünün tümünü çalıştırır ancak yazı tipi başına PDFium katmanını çalıştırmaz, bu nedenle yazı tipi kararı kaba sezgisel yönteme düşer. Mantıklı bir mimari, ValidatePdfXCompliance işlevini ucuz ilk kapı olarak kullanır ve tam TPdf.ValidatePdfX işlevini onu geçen dosyalar için saklar

Bu doğrulayıcının bittiği ve tam bir uçuş öncesi (preflight) kontrolünün başladığı yer

Uçuş öncesi (preflight) araçlarında dürüstlük önemlidir, bu nedenle sınır burasıdır. ValidatePdfX; tanımlama işaretçilerini, yapısal yasakları, sayfa geometrisi anahtarlarını, Trapped beyanını ve bireysel metin nesnelerine kadar yazı tipi gömmeyi doğrular. Toplam mürekkep kapsamını ölçmez, her renk alanının iddia edilen varyant için yasal olduğunu doğrulamaz (örneğin X-1a'nın CMYK-yalnızca kuralı), görüntü çözünürlüğünü satır sıklığına karşı kontrol etmez veya üst üste baskı (overprint) ve şeffaflık düzleştirme (flattening) davranışını değerlendirmez —bunlar renk yönetimli bir uçuş öncesi motoruna ihtiyaç duyar ve birimin kendi belgeleri nihai sertifikasyon için onunla eşleştirilmesini söyler. İki katmanlı kontrolün size sunduğu şey, yapısal ve erken tespit edilebilir olan %80'lik reddetme oranıdır, matbaadan yarın gelecek e-posta yerine kendi Delphi kodunuz içinde milisaniyeler içinde yakalanır

Hem doğrulama katmanları hem de uyumlu çıktı üretmek için PDF/X işaretçi yerleştirme API'leri, ve aynı mimariyi paylaşan PDF/A, PDF/UA, PDF/E ve PDF/VT doğrulayıcıları, baskı öncesi geçiş kontrolünden oluşturmaya kadar tek bir bileşen olarak Delphi ve C++Builder için PDFium Bileşeni'nde sunulmaktadır