Teknik Makale

Delphi'de PDFium VCL ile PDF/VT Değişken Veri Baskısı

İşlemsel baskı yapan matbaa, 80.000 sayfalık ekstre işinizi tek satırlık bir reddedişle geri gönderiyor: "PDF/VT değil, RIP önbellekleyemiyor". Dosya masanızdaki her görüntüleyicide düzgün açılıyor, renkler doğru, veriler doğru birleşmiş. Fakat dijital baskı makinesinin istediği şey bunlar değildir. Yüksek hızlı değişken veri baskısı, baskı makinesinin 1. sayfadaki müşteri logosu bloğunun 40.000. sayfadakiyle bayt bayt aynı nesne olduğunu tanıyıp bunu bir kez işleyebilmesine ve yeniden kullanabilmesine bağlıdır. PDF/VT bu vaadi makine tarafından doğrulanabilir kılan standarttır ve "doğru görünüyor" ifadesi tam bir tuzaktır; çünkü RIP'in okuduğu yapı ekranda görünmez

PDFiumPas, bu yapıyı TPdf üzerinde küçük bir yüzeyle açar: SaveAsPdfVT bunu yazar, ValidatePdfVT bunu denetler. Bu makale, bu iki yöntemin diske gerçekte ne yazdığını ve neyi incelediğini, ISO 16612-2'nin ilk bakışta göründüğünden nerede daha katı olduğunu ve hangi parçaların, müşteriye fatura kesebileceğiniz tam bir preflight yerine, dürüst yapısal dayanaklar olduğunu anlatır

PDF/VT neyi standartlaştırır ve PDF/X neden önce gelir

PDF/VT (ISO 16612-2:2010) yeni bir dosya biçimi değildir. PDF/X dosyasının üzerine eklenmiş bir optimizasyon metadata katmanıdır ve bu sıralama taşıyıcı unsurdur. Standart üç uyumluluk seviyesi tanımlar, ancak bunlardan yalnızca ikisi bir PDF dosyasını adlandırır: PDF/VT-1, tek ve kendi kendine yeten belge; ve PDF/VT-2, sayfaların paylaşılan dış kaynaklara başvurduğu dosya kümesi modeli. Görebileceğiniz üçüncü simge olan PDF/VT-2s ise dosya düzeyi bir değer değildir; Annex A'da tanımlanan MIME stream header içinde yaşar. Bir belgenin XMP'sine GTS_PDFVTVersion = "PDF/VT-2s" damgalayan kod görürseniz, o kod yanlıştır

Tek dosya için pazarlık kabul etmeyen kural, PDF/X tabanıdır. ISO 16612-2 §6.2.1, her PDF/VT-1 dosyasının aynı zamanda geçerli bir PDF/X-4 dosyası olmasını ister. PDF/VT-2 dosya kümesi ise §6.2.2 gereği PDF/X-4p, PDF/X-5g veya PDF/X-5pg üzerine oturmalıdır. Bu yüzden PDF/VT yazıcısı sadece birkaç kimlik anahtarı ekleyemez: bütün PDF/X-4 işaret kümesini de taşımak zorundadır; yani bir OutputIntent, gömülü ICC hedef profili, buna uyan XMP ve document Info girdileri, trailer içinde bir /ID ve şifreleme olmaması gerekir. Bunlardan herhangi birini atlarsanız, PDF/VT dediğini iddia eden ama uyumlu bir tüketici tabanı denetlediği anda çöken bir dosya elde edersiniz. PDFiumPas, PDF/X-4 katmanını PDF/VT kaydının parçası sayar; bu yüzden ayrıca SaveAsPdfX çağırmanız gerekmez, enjektör iki katmanı da tek geçişte yazar

SaveAsPdfVT ile dosya yazma

En küçük çağrı, etkin bir belgeden başka bir şey istemez; çünkü TPdfVTSaveOptions.Default yerleşik sRGB ICC profili ile pvc1 uyumluluğunu sağlar. Kaydetme dahili olarak üç adım yürütür: güvenliği kaldırır (şifreli object stream içine düz metin işaretleri enjekte etmek akışı bozar), belgenin mevcut Info dictionary'si ile trailer /ID değerini marker set içine köprüler; böylece XMP ve Info değerleri uyumlu kalır; ardından PDF/X-4 ve PDF/VT nesnelerini incremental update ile ekler

var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    if Pdf.LoadFromFile('statements-merged.pdf') then
    begin
      // Default options: built-in sRGB OutputIntent, PDF/VT-1, synthesised DPart
      if Pdf.SaveAsPdfVT('statements-pdfvt.pdf') then
        Writeln('PDF/VT-1 written')
      else
        Writeln('Save failed (document not active?)');
    end;
  finally
    Pdf.Free;
  end;
end;

Gerçek üretim çıktısında neredeyse her zaman OutputIntent'i genel sRGB geri dönüşü yerine baskı makinenizin karakterizasyonuyla geçersiz kılmak istersiniz. ICC baytlarını ve koşul tanımlayıcılarını TPdfVTSaveOptions üzerinden sağlayın:

var
  Pdf: TPdf;
  Opt: TPdfVTSaveOptions;
  Icc: TBytes;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.LoadFromFile('directmail-merged.pdf');
    Icc := LoadIccProfile('GRACoL2013_CRPC6.icc');  // your own loader

    Opt := TPdfVTSaveOptions.Default;
    Opt.Conformance := pvc1;            // pvc2 is normalised to pvc1 on write
    Opt.IccProfileData := Icc;
    Opt.OutputConditionIdentifier := 'CGATS21_CRPC6';
    Opt.OutputCondition := 'Commercial print, coated, CRPC6';
    Opt.RegistryName := 'http://www.color.org';
    Opt.Title := 'Spring 2026 Direct Mail Run';
    Opt.Trapped := ptvFalse;           // PDF/X Info /Trapped state

    Pdf.SaveAsPdfVT('directmail-pdfvt.pdf', Opt);
  finally
    Pdf.Free;
  end;
end;

Bu parçacıktaki bir ayrıntı, tartışılabilir bir kısıt değil bilinçli bir korkuluktur. Opt.Conformance := pvc2 ayarlamak PDF/VT-2 dosyası üretmez. Yazıcı, pvc1 dışındaki her isteği yeniden pvc1 değerine normalleştirir; çünkü PDF/VT-2 dosya kümesi biçimidir ve tek bir çıktı belgesi ekleyen tek dosyalı yazıcı, §6.2.2'nin istediği dış kaynak kümesini fiziksel olarak oluşturamaz. pvc2 değeri okuma yolu içindir; böylece ValidatePdfVT mevcut bir dosya kümesi belgesini tanıyıp raporlayabilir; yazma hedefi değildir

DPart ağacı: RIP'in gerçekte okuduğu yapı

PDF/VT'nin kalbi Document Part (DPart) hiyerarşisidir. Bu yapı, baskı makinesinin uzun bir çalışmayı kayıtlara bölmesine, kayıtları alıcılar ya da posta paketleri halinde gruplamasına ve aşağı akış ekipmanının her parçayı yönlendirip faturalandırabilmesi için Document Part Metadata eklemesine izin verir. ISO 16612-2 §6.5 bunun kablolamasını tanımlar: catalog bir /DPartRoot taşır, kök DPart düğümü /DPartRootNode ve her hiyerarşi seviyesini adlandıran bir /NodeNameList içerir, yaprak DPart'lar sayfa ağacının aralıklarını kapsar ve bir parçaya ait her sayfa, sayfa düzeyindeki /DPart girdisiyle kendi yaprağına geri işaret eder

Kaynak belgenizde kullanılabilir bir hiyerarşi zaten varsa, SaveAsPdfVT bunu korur. Yoksa yazıcı en küçük işleyen yapıyı sentezler: mevcut sayfa ağacını sırayla kapsayan tek belge düzeyi DPart, her canlı sayfa nesnesine eklenen bir /DPart geri bağlantısı ve tek seviyeli bir /NodeNameList [/Document]. Bu asgari ağacın ne olduğu konusunda kendinize dürüst olun. Bu, §6.5'in şekil gereksinimlerini sağlayan yapısal bir ankordur; iş verisi değildir. Alıcıları, posta parçası sınırlarını veya ürün partilerini icat edemez; çünkü bu bilgi kaynakta hiç yoktur. Alıcı başına veriniz varsa, daha derin DPart ağacını kendiniz kurmanız ve oluşturduğunuz seviyelere uyacak şekilde /NodeNameList yapısını genişletmeniz beklenir

Anahtar varlığı denetiminin ötesine geçen doğrulama

ValidatePdfVT, üç şey taşıyan bir TPdfVTValidationResult kaydı döndürür: algılanan Conformance, bir Issues issue kümesi ve yalnızca uyumluluk gerçek seviye olduğunda ve issue kümesi boş olduğunda true olan IsCompliant yardımcısı. Issue enum'u özellikle ayrıntılı tutulmuştur; böylece başarısız sonuç size sadece "geçersiz" demek yerine hangi maddeyi kaçırdığınızı söyler:

var
  Pdf: TPdf;
  Res: TPdfVTValidationResult;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.LoadFromFile('statements-pdfvt.pdf');
    Res := Pdf.ValidatePdfVT;

    if Res.IsCompliant then
      Writeln('PDF/VT compliant: ', VTLevelName(Res.Conformance))
    else
    begin
      if pvviMissingDPartRoot in Res.Issues then
        Writeln('DPart hierarchy missing or unusable');
      if pvviMissingPdfXIdentifier in Res.Issues then
        Writeln('PDF/X-4 base identifier absent');
      if pvviMissingOutputIntent in Res.Issues then
        Writeln('OutputIntent / ICC profile missing');
      if pvviEncryptionPresent in Res.Issues then
        Writeln('Encrypted - PDF/X forbids this');
    end;
  finally
    Pdf.Free;
  end;
end;

Derinlemesine anlaşılması gereken iki denetim, uyumluluk eşleştirmesi ile DPart yürüyüşüdür; çünkü ikisi de eskiden fazla gevşekti ve standarda uyacak şekilde sıkılaştırıldı. Eşleştirme tarafında doğrulayıcı yaklaşık eşleşme değil, tam eşleşme yapar: PDF/VT-1 dosyası yalnızca PDF/X-4 tabanı üzerinde kabul edilir, PDF/VT-2 dosyası ise yalnızca PDF/X-4p, PDF/X-5g veya PDF/X-5pg üzerinde kabul edilir. PDF/X-1a tabanı üzerine konmuş PDF/VT-1 işareti sessizce geçirilmez, raporlanır

Asıl titizlik DPart yürüyüşünde yaşar. Catalog içinde /DPartRoot anahtarının bulunması yeterli değildir; çünkü sahte boş nesne ya da sayfa bağlantısı olmayan nesne yine de tüketilemez. HasValidDPartHierarchy ile özyineli ValidateDPartNode, yapının tamamını izler: üst bağlantıları takip eder, yinelemeli çocukları ve döngüleri reddeder, /Start ile /DParts öğelerinin birbirini dışladığını zorlar ve yaprak sayfa aralıklarının, her sayfanın /DPart girdisi kendisini içeren yaprağa işaret edecek şekilde, sayfa ağacını derinlik öncelikli sırada kapsamasını ister. Bütün bu dahili kusurlar, herkese açık enum'u büyütmek yerine tek bir pvviMissingDPartRoot issue biti altında toplanır; dolayısıyla bu bayrağı kelimenin tam anlamıyla "kök anahtar yok" değil, "DPart hiyerarşisi kullanılamaz" diye okuyun

Doğrulayıcının artık zorladığı üç sözdizimsel tuzak

§6.5 Table 4'e karşı yapılan art arda geçişler, önceki sürümlerin kabul ettiği ama standardın kabul etmediği şekilleri ortaya çıkardı. Bunlar özellikle elle kurulmuş DPart ağaçlarında sık görülen hatalardır, bu yüzden açıkça anılmaya değerler:

  • /DParts dış dizi içinde dizilerden oluşur, düz dizi değildir Dış dizinin her öğesi, kendi başına dolaylı başvuru dizisi olmalıdır. Düz bir /DParts [9 0 R] reddedilir; uyumlu şekil /DParts [[9 0 R] [10 0 R]] biçimidir. Bu, hiyerarşik olmayan yapının geçerli seviye gibi davranmasını engeller
  • /End yalnızca gerçekten çok sayfalı aralığı işaretler Yaprak DPart, /End öğesini yalnızca /Start varsa taşıyabilir ve /End, sayfa ağacı sıralamasında /Start sonrasına düşmelidir. Artık bozuk bir /Start 3 0 R /End 3 0 R, tek sayfalık parça gibi okunmak yerine hiyerarşiyi kullanılamaz hale getirir
  • /NodeNameList adları, PDF name unescape işleminden sonra XML NMTOKEN olarak hayatta kalmalıdır /Bad#20Name gibi bir ad, açıldığında boşluk içeren bir ada dönüşür ve bu geçerli token değildir. Uygulama hafif bir ASCII denetimi yapar (harfler, rakamlar, ., -, _, : ve ASCII dışı baytlar); bu sayede meşru yerelleştirilmiş ya da üreticiye özgü adları reddetmeden boşluk ve ayraç hataları yakalanır

XMP işaretleri: aynı özelliği yazmanın iki yolu

PDF/VT tanımlaması, pdfvtid ad alanı altındaki XMP içinde yaşar; özellikle GTS_PDFVTVersion ve GTS_PDFVTModDate, ayrıca standart xmp:CreateDate ile xmp:ModifyDate yanında bulunur. Saf okuyucularda sahte "eksik" raporlarına yol açan bir incelik şudur: bunların herhangi biri iki biçimde serileştirilebilir: öğe metni olarak (<pdfvtid:GTS_PDFVTVersion>PDF/VT-1</pdfvtid:GTS_PDFVTVersion>) ya da description öğesi üzerinde RDF özniteliği olarak. PDFiumPas iki biçimi de okur; bu yüzden başka aracın öznitelik tarzında yazdığı dosya cezalandırılmaz. Ayrıca §6.3 tutarlılık kuralını da zorlar: GTS_PDFVTModDate değeri xmp:ModifyDate ile aynı olmalıdır; uyuşmazlık pvviModDateMismatch yükseltir

Aynı maddeden bir başka kural daha: bilinmeyen GTS_PDFVTVersion değeri, yeniden pvcUnknown olarak korunur; pvcNone değerine geri katlanmaz. Bu ayrım işletimsel açıdan önemlidir. pvcNone, "hiç PDF/VT işareti yok, sıradan PDF" anlamına gelir; pvcUnknown ise "bir şeyin damgaladığı ama bu doğrulayıcının tanımadığı bir sürüm var" demektir (bunların arasında PDF/VT-2s durumu da vardır). İkisini aynı kovada toplamak, bozuk dosyayı sıradan belgeyle aynı sepete saklar

Garantinin bittiği yer

Bu yöntemlerin ne vaat ettiğinin sınırını net konuşmaya değer; çünkü değişken veri baskı uyumluluğunun arkasında gerçek para vardır. DPart ve eşleştirme denetimleri bayt düzeyinde yapısal doğrulamadır. Optimizasyon iskeletinin, PDF/X-4 taban işaretlerinin, OutputIntent'in ve XMP'nin mevcut ve iç tutarlı olduğunu doğrular. Bunlar içerik düzeyinde PDF/X-4 preflight değildir: her rengin ilan edilen çıktı koşulu içinde olduğunu, tüm fontların gömülü olduğunu ya da yasaklı saydamlık-karıştırma köşe durumlarından hiçbirinin sızmadığını doğrulamaz. Sözleşmeli baskı makinesine göndereceğiniz iş için PDFiumPas'ın yapısal doğrulamasını özel bir PDF/X preflight motoru ve deneme baskısıyla eşleyin; diğer her uyumluluk iddiasında yapacağınız gibi. Yapısal katman, RIP önbelleğini sessizce bozan arızaları yakalar; tam denetimin bir yarısıdır, bütünü değildir

Bu denetimleri daha geniş bir yayın kapısına yerleştiriyorsanız, aynı bayt düzeyindeki tarama yaklaşımı kütüphanenin diğer standart çalışmalarını da destekler; buna dosya preflight'a gelmeden önce nesne ve cross-reference stream doğrulaması ve belgenin baştan RIP dostu olmasını sağlayan Form XObject ile yeniden kullanılabilir sayfa damgaları arkasındaki paylaşılan nesne disiplini de dahildir. Burada açıklanan PDF/VT ve PDF/X kaydetme ile doğrulama API'leri, PDFium VCL component kapsamında Delphi ve C++Builder için sunulur; ürün sayfası eksiksiz uyumluluk başvurusunu taşır