Teknik Makale

PDF/A Uygulama Sınırları ve Yazı Tipi Kodlama Denetimleri

Bir fatura arşivi temiz işleyebilir, basılabilir, her yazı tipini gömebilir ve geçerli bir çıktı niyeti taşıyabilir; yine de bir PDF/A doğrulayıcısı onu, bir kaynak sözlüğü 4.096 girdi taşıdığı için reddeder. Tavan 4.095'tir. PDF/A, PDF Reference'tan küçük bir uygulama sınırları tablosu devralır ve herhangi birinin bir birim üzerinde olan bir dosya, hiçbir görüntüleyici şikâyet etmese de uyumluluktan düşer

Aynı "ama iyi görünüyor" kovasına düşen öbür başarısızlık, bir /Encoding girdisi taşıyan sembolik bir TrueType yazı tipidir. Onay kutuları, barkodlar ve dingbatlar ekranda doğru çıkar ve doğrulayıcı yine de yazı tipi sözlüğünü işaretler. İki sorun da, bir kişinin sayfada görebileceği hiçbir şeyde değil nesne çizgesinde yaşar; bu yüzden bir dosya arşive girmeden önce mekanik olarak denetlenmeye değerler

PDF/A'nın devraldığı sayılar ve sınırın oturduğu yer

PDFium Component her PDF/A doğrulamasında dört kapsayıcı sınırını denetler: bir ad belirteci en fazla 127 bayt uzunluğunda olabilir, bir dizi en fazla 8.191 öge tutabilir, bir sözlük en fazla 4.095 girdi tutabilir ve diziler ile sözlükler en fazla 28 düzey derinlemesine iç içe geçebilir. Dört sınırın hepsi dâhildir. 127 baytlık bir ad geçer, 128 baytlık bir ad düşer; tam 8.191 ögeli bir dizi geçer, bir öge daha onu düşürür

Ad, dizi ve sözlük sınırları, uyumlu bir okuyucudan yalnızca o boyutlardaki yapıları ele alması istendiği çağdan gelir. Güncel bir masaüstü görüntüleyici çok daha büyük bir diziyi şikâyet etmeden açabilir; ama arşivlik bir dosyanın, henüz kimsenin karşı test etmediği okuyucularda — bir kayıt sisteminin hâlâ çalıştırıyor olabileceği eski sürümler dâhil — açılması beklenir. PDF/A bu yüzden tarihsel tavanları gevşetmek yerine korur

Dâhil ile hariç ayrımı, elle yazılmış denetimlerin genellikle yanlış gittiği yerdir. Count > 8191 yerine Count >= 8191 yazılmış bir test, uyumlu dosyaları reddeder ve uyumlu dosyaları reddeden bir doğrulayıcı yine de inanılan olur; böylece üretici, hiç sahip olmadığı bir sorun için "düzeltilir". FPdfPdfa unitindeki ValidatePdfACompliance fonksiyonu sayımı katı biçimde sınırın ötesine taşır ve dosya baytlarını doğrudan okur; denetim, PDFium DLL'ini yüklemeden çalışır

Dört kapsayıcı ISO 19005-1 Annex C uygulama sınırının şeması: Delphi'den saf bir bayt taramasıyla doğrulanır: 127 baytlık ad belirteçleri, 8191 dizi öğesi, 4095 sözlük girişi ve 28 düzey kapsayıcı iç içe yerleştirme
Dört sınırın tamamı dâhildir — sınır değeri kendisi doğrulanır ve yalnızca sıradaki birim başarısız olur
uses
  System.Classes, FPdfPdfa;

procedure CheckContainerLimits(const FileName: string; Log: TStrings);
var
  Src: TFileStream;
  Res: TPdfAValidationResult;
begin
  Src := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfACompliance(Src);   // bayt taraması, PDFium DLL'i gerekmez
    if pvaiNameOverLimit in Res.Issues then
      Log.Add('A name token is longer than 127 bytes');
    if pvaiArrayOverLimit in Res.Issues then
      Log.Add('An array holds more than 8191 elements');
    if pvaiDictOverLimit in Res.Issues then
      Log.Add('A dictionary holds more than 4095 entries');
    if pvaiNestingOverLimit in Res.Issues then
      Log.Add('Arrays and dictionaries nest deeper than 28 levels');
    if Res.IsCompliant then
      Log.Add('No PDF/A findings');
  finally
    Src.Free;
  end;
end;

Dört bayrak, TPdfAValidationResult üzerindeki Issues kümesinin üyeleridir; diğer her PDF/A bulgunun yanında. IsCompliant, yalnızca bir PDF/A kimlendirmesi bulunduğunda ve küme boşken True döndürür. Hiç PDF/A kimlendirmesi taşımayan bir dosya, katı PDF/A-1 kural setine karşı denetlenir; kapsayıcı sınırları ona hâlâ uygulanır

Bu sınırlara gerçekte hangi üreticiler çarpar?

Neredeyse daima yapıyı bir döngüde kuran olanlardır. Birkaç bin alana sahip bir form, 8.191 girdiyi geçen bir AcroForm /Fields dizisi ya da sayfa /Annots dizisi üretir. Ürettiği her nesne için bir yazı tipi ya da görüntü kaynağı kaydeden ve var olan bir girdiyi asla yeniden kullanmayan bir rapor motoru, bir /Resources alt sözlüğünü 4.095 anahtarın ötesine büyütür. İç içe bir veri modeli üzerinde özyineleyerek üretilen yapı ağacına sahip etiketli bir belge, kimse bakmadan 28 düzeyi geçebilir

Uzun adların kökeni farklıdır: verilerin ad belirteçlerine paketlenmesi. Bir müşteri referansından adını alan bir separation colorant, bir dosya yolundan adını alan isteğe bağlı içerik grubu, hiyerarşinin altı düzeyinden kurulan bir alan etiketi. Adlar üretmesi bedavadır ve vurgulu karakterli bir UTF-8 etiket işin içine girdiğinde 127 bayt hızla biter; o karakterlerin her biri iki ya da üç bayt alır. 127 bayt, / başlatıcısından sonra, bir sonraki sınırlayıcıya ya da boşluğa kadar sayılır

Çare her seferinde yapısaldır. Diziyi bölün, sözlüğü bölün, özyinelemeyi düzleştirin, adı kısaltın. Dosyaya PDF/A işaretçileri enjekte etmek bu bulguların hiçbirine yardım edemez; sorun nesne çizgesinin şeklidir ve XMP üst verisi hakkında hiçbir şey söylemez

Tarama ögeleri ve girdileri nasıl sayar

Sayım kuralı, bir bulguyu bir hex düzenleyicide yeniden üretmeye çalıştığınızda önem taşır. İç içe bir dizi ya da sözlük, atasının tek bir ögesi olarak sayılır; sınır böyle ifade edilir: 1.000 ögeli dizilerden oluşan 10 ögeli bir dizi sorun değildir, çünkü tek bir dizi bile 8.191'i aşmaz. Sözlüğün içinde yalnızca anahtarlar sayılır; tesadüfen bir ad olan bir değer, girdi toplamını şişirmez. <FEFF0041> gibi hex dizgeler tek simge olarak atlanır; açı parantezleri asla sözlük iç içeliği sanılmaz

Tarama bütün dosyanın nesne yapısını kapsar; sayımdan önce genişletilen sıkıştırılmış nesne akışlarının içinde saklanan nesneler dâhil. Akış gövdelerinin kendileri — sayfa içeriği ve görüntü verisi gibi — önce boşaltılır; böylece tesadüfen bir / ya da [ içeren bir ikili bayt dizisi, yanlış bir ad ya da dizi bulgusu tetikleyemez

Aynı doğrulamanın kendi bayrağı altında bildirdiği bitişik bir sayısal sınır vardır. Dosya PDF/A-2 ya da sonraki bir bölüm bildirdiğinde, 32 bit işaretli aralığın dışında — yani −2.147.483.648 ile 2.147.483.647 aralığının dışında — her tamsayı simgesi için pvaiIntegerImplementationLimit yükselir. Bayt ofsetlerini ya da büyük sayaçları düz tamsayılar olarak yazan üreticiler alışılmış kaynaktır

Sembolik bir TrueType yazı tipi neden /Encoding taşımamalı?

ISO 19005-1 §6.3.7, sembolik bir TrueType yazı tipi için karakter eşlemesi olarak yalnızca yazı tipi programının yerleşik cmap'ine izin verir. Sembolik bir yazı tipi kodları kendi şartlarında gliflere eşler; Symbolic bayrağının bildirdiği budur. Bir /Encoding girdisi ekleyin ve dosya, artık "0x41 baytı hangi glifi seçer" sorusuna iki yanıt verir; hangisinin kazandığını söyleyen dosyada hiçbir şey yoktur. Okuyucular çelişkiyi farklı çözer ve aynı onay kutusu bir görüntüleyicide onay işareti, öbüründe A harfi olarak çıkabilir

ValidatePdfACompliance, /Subtype /TrueType olan her yazı tipi sözlüğünü bulur, /Encoding anahtarı taşıyıp taşımadığına bakar ve sonra yazı tipi tanımlayıcısındaki /Flags girdisinin 3. bitini (değer 4) okur. Tanımlayıcı, satır içi yazılmış ya da dolaylı bir nesne olarak başvurulmuş olsun izlenir. /WinAnsiEncoding'ini ya da /MacRomanEncoding'ini koruyan sembolik olmayan bir TrueType yazı tipi bu denetimle işaretlenmez; sembolik olmayan yazı tipleri için PDF/A tam olarak o kodlamayı bekler

Simbolik bir TrueType yazı tipini (/Encoding girişi iki rekabet eden glif eşlemesi oluşturur) WinAnsiEncoding'i meşru şekilde koruyan simgelemeyen bir yazı tipiyle Delphi bir PDFium Component kontrolünde karşılaştıran şema
Kontrol, sembolik bayrağı yazı tipi tanımlayıcısından okur ve çelişki üzerinde ateşler; bir kodlamanın varlığı üzerinde değil
// Res, ilk örnekteki gibi ValidatePdfACompliance'dan gelir
if pvaiSymbolicTrueTypeEncoding in Res.Issues then
  Log.Add('A symbolic TrueType font carries /Encoding; PDF/A admits only ' +
    'the font''s built-in cmap (ISO 19005-1 6.3.7)');

// Farklı tetikleyicili komşu bir yazı tipi kuralı: SİMGELEMEYEN
// Encoding sözlüğü /Differences taşıyan bir TrueType yazı tipi
if pvaiTrueTypeDifferencesForbidden in Res.Issues then
  Log.Add('A non-symbolic TrueType Encoding dictionary carries /Differences');

Bu kurala takılan yazı tipleri öngörülebilirdir: Symbol, Wingdings, barkod yazı tipleri, simge yazı tipleri. İş belgelerinde onay kutuları, puan yıldızları ve barkodlar olarak görünürler ve her TrueType yazı tipine aynı gözle bakan bir alt kümeleme adımı onlara da bir kodlama yazar. Bir doğrulayıcı "yazı tipi sorunu" dediğinde kimsenin bakmadığı yazı tipleri de onlardır; gövde metni yazı tipleri önce denetlenir

Ters hata da sahada görünür: gerçekten WinAnsi kodlu sıradan bir metin yazı tipine Symbolic bayrağını ayarlayan bir üretici. PDF/A denetimi maddenin yazıldığı gibi izler ve o yazı tipini bildirir; burada onarım tanımlayıcıya aittir: kodlamayı silmek yerine tanımlayıcıdaki bayrağı düzeltin. Yanlış etiketlenmiş bir metin yazı tipinden /Encoding'i kaldırmak, metni alt kümenin tesadüfen içerdiği hangi cmap varsa ona muhtaç bırakır; başladığınızdan daha kötü bir dosya budur. Çelişkinin hangi yarısını kaldıracağınıza karar vermeden önce yazı tipinin glif setine bakın

Bulguları bir preflight raporunda okumak

Preflight rapor uniti Enterprise sürümüyle gelir; ValidatePdfACompliance her sürümdedir. FPdfPreflightReport unitindeki rapor, dört kapsayıcı sınırını Yapı (Structure) kategorisine, sembolik TrueType kodlama sorununu İçerik (Content) kategorisine ayırır. O ayrım kasıtlıdır. Yapı bulguları genellikle üreticiyi sürdüren kişiye döner; içerik bulguları genellikle yazı tiplerini ve şablonları sağlayan kişiye gider ve ikisini harmanlayan bir rapor, iki kişiyi de kendilerine ait olmayan sorunların peşine gönderir

Her sorun, Code olarak enum adını ve çareyi somut terimlerle adlandıran bir öneri taşır; örneğin "Sözlükleri bölün, hiçbiri 4095'ten fazla girdi taşımasın". Yapı kategorisinin iki bulgu daha taşıdığını unutmayın: A Düzeyi iddiasında eksik etiketli yapı ve PDF/A-1 dosyasında PDF 1.5 çapraz başvuru ya da nesne akışları; yalnızca dört sınırı istiyorsanız koda göre süzün

uses
  System.SysUtils, System.Classes, PDFium, FPdfPreflightReport;

procedure ReportStructureFindings(const FileName: string; Log: TStrings);
var
  Pdf: TPdf;
  Report: TPdfPreflightReport;
  Issues: TPdfPreflightIssues;
  I: Integer;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Active := True;          // bu yol PDFium DLL'ini yükler
    if not Pdf.Active then
      Exit;
    Report := BuildPdfPreflightReport(Pdf, [ppsPdfA]);
    Issues := Report.FilterByCategory('Structure').FlattenIssues;
    for I := 0 to High(Issues) do
      Log.Add(Issues[I].Code + ': ' + Issues[I].Recommendation);
    Report.SaveJsonToFile(ChangeFileExt(FileName, '.preflight.json'));
  finally
    Pdf.Free;
  end;
end;

İki yol hafif farklı soruları yanıtlar. BuildPdfPreflightReport, TPdf.ValidatePdfA üzerinden gider; o, açık belgeyi geçici bir kopyaya kaydeder, aynı bayt taramasını o kopyada çalıştırır ve sonra PDFium motoru gerektiren denetimleri — bir sayfada kullanılıp gömülmemiş yazı tipleri gibi — ekler. ValidatePdfACompliance, diskteki tam baytları yargılar. Uygulama sınırları için güvenilen yol, orijinal dosya üzerindeki bayt yoludur: arşivlemek üzere olduğunuz dosya hakkında rapor verir ve yerel bir DLL'in dağıtılamadığı bir derleme adımında ya da kilitli bir hizmette çalışır. Motor düzeyi denetimleri ve bir hatt için JSON, SARIF ya da JUnit çıktısını da istiyorsanız rapor yolunu kullanın

Buradan nereye

Bu denetimleri bir klasör boyunca çalıştırmak, hangi bulguların bir sürümü bloke ettiğine karar vermek ve sonucu insanların okuyacağı bir rapora çevirmek, Delphi'de PDF/A preflight doğrulaması gezintisinde ve toplu preflight rapor CLI'ında kapsanır. Başta hangi PDF/A bölümünün ve uyumluluk düzeyinin hedefleneceği, PDF/A arşiv uyumluluğu notlarının konusudur; o seçim, diğer şeylerin yanı sıra tamsayı sınırının uygulanıp uygulanmayacağına karar verir

Burada gösterilen doğrulayıcılar, PDFium motorunun üzerindeki işleme, form doldurma ve düzenlemeyle birlikte Delphi, C++Builder ve Lazarus için PDFium Component ile gelir. PDFium Component ürün sayfası, desteklenen uyumluluk standartlarını, sürümleri ve derleyici sürümlerini listeler