Teknik Makale

Delphi'de PDF/A için Mevcut PDF'lere Eksik Yazı Tiplerini Gömme

losLab PDF Kütüphanesi, önceden yüklenmiş bir PDF'in eksik yazı tipi programlarını tek bir çağrıyla gömebilir: EmbedMissingFonts belgedeki her yazı tipi sözlüğünü tarar, BaseFont adına göre eşleşen yüklü sistem yazı tipini bulur ve yazı tipi programını dosyaya geri yazar. Yazı tipi gömme konusunda PDF/A doğrulamasından geçemeyen üçüncü taraf belgeleri onaran ekipler için bu, 00030 ön uçuş hatasını ortadan kaldıran çözümdür

Bu senaryo ne yazık ki oldukça yaygındır. Bir arşiv alım iş hattı, tedarikçilerden, müşterilerden veya bir tarama bürosundan PDF'ler alır; belgeler binadaki her bilgisayarda sorunsuz şekilde işlenir; ancak ardından PDF/A doğrulayıcı, dosya başına bir kez tekrarlanan aynı şikayetle tüm grubu reddeder: en az bir yazı tipi gömülü değildir. Kaynak tarafta hiç kimse dosyaları yeniden oluşturmayacağından, iş hattının bunları onarması gerekir. Bu makale bu onarım yolunu ele almaktadır. Bu yazı, PDF/A ve PDF/UA ihlallerinin tespitini kapsayan ön uçuş makalesinin tamamlayıcısıdır: o makale hangi belgelerin bozuk olduğunu söyler, bu makale ise en sık karşılaşılan bozukluk şeklini düzeltir

PDF/A neden her yazı tipinin gömülmesini gerektirir?

ISO 19005-1 §6.3.4, uyumlu bir belge tarafından kullanılan her yazı tipinin kendi yazı tipi programını dosya içinde taşımasını gerektirir, çünkü PDF/A'nın tüm vaadi yeniden üretilebilirliktir: belge, elli yıl sonra kendisini üreten makineyle hiçbir yazı tipini paylaşmayan bir makinede bile tamamen aynı şekilde işlenmelidir. Gömülmemiş bir yazı tipi, görüntüleme sisteminde bir yerde Arial yazı tipini bulma talimatıdır ve standardın görüşüne göre "görüntüleme sisteminde bir yer" bir arşiv garantisi değildir. İkame yazı tipinin glifleri, metrikleri ve kapsamı ne olursa olsun, okuyucunun elde edeceği şey budur ve yazarın gördüğü şeyle aynı olmayabilir

Tarihsel suçlu Standart 14 kuralıdır. PDF 1.0, her görüntüleyicinin Helvetica, Times, Courier, Symbol ve ZapfDingbats yazı tiplerini içereceğini taahhüt etmişti, bu nedenle oluşturucular bu yazı tiplerine adıyla başvurmayı ve hiçbir şey gömmemeyi öğrendiler ve otuz yıllık araçlar hala tam olarak bunu yapıyor. losLab PDF Kütüphanesi bu gereksinimi o kadar ciddiye alır ki, PDF/A oluşturma modunda AddStandardFont kasıtlı olarak etkisiz bir işlemdir (no-op): kütüphane Standart 14 yazı tipi programlarını barındırmaz, sahip olmadığı şeyi gömemez ve uyumluluk iddiasındaki bir belgeye gömülmemiş bir referans yazmayı reddeder. Yazı tipi seçmeden 0 döndürür, bu nedenle bir PDF/A belgesi bunun yerine gömülü olarak AddTrueTypeFont kullanmalıdır ve herhangi bir Embed=0 talebi, PDF/A modu etkinken sessizce Embed=1 durumuna yükseltilir. Bu, yazıcı tarafıdır. Daha zor olan sorun ise okuyucu tarafıdır: başkasının çoktan yazdığı, oluşturmadığınız yazı tipi sözlükleriyle dolu bir belge

EmbedMissingFonts yüklenmiş bir belgeyi nasıl onarır?

losLab PDF Kütüphanesi, yazı tiplerini yeniden oluşturmak yerine yerinde onarır. Bir PDF oluşturucu gömülmemiş bir TrueType yazı tipi yazdığında, ürettiği FontDescriptor sözlüğü zaten tamamlanmıştır: FontName, FontBBox, Flags, Ascent, Descent, StemV, hepsi mevcuttur. Onu gömülü bir yazı tipinden ayıran tek şey, gerçek yazı tipi programını tutan /FontFile2 akış referansı olan tek bir girdinin olmamasıdır. Bu nedenle EmbedMissingFonts, yazı tipi sözlüğüne, kodlamaya, genişlikler dizisine veya yazı tipine kaynak adıyla başvuran herhangi bir içerik akışına dokunmaz. Sistemden eşleşen yazı tipi programını okur, yeni bir akış nesnesine sıkıştırır ve halihazırda orada olan FontDescriptor'a tek bir /FontFile2 referansı (veya CIDFontType0 yazı tipleri için /FontFile3) ekler. Belgenin sayfalarının işaret ettiği her şey tam olarak olduğu yerde kalır; bu da işlemi kontrolünüz dışındaki dosyalarda çalıştırmayı güvenli kılan şeydir

Kapsam, pratikte karşılaşacağınız her iki yazı tipi mimarisini de içerir: basit TrueType yazı tipleri ve CJK metinleri ile modern Unicode çıktısı için üretilen kompozit Type0/CID yazı tipleri. Tarama işlemi, sayfa sayfa kaynak aramasına güvenmek yerine belgenin nesne ağacındaki her Font sözlüğünü kasıtlı olarak listeler, böylece açıklamalardan başvurulan veya sayfalar arasında paylaşılan yazı tipleri de yakalanır. API, yüklenen belge üzerinde tek bir çağrıdır

var
  PDF: TPDFlib;
  Repaired: Integer;
begin
  PDF := TPDFlib.Create;
  try
    if PDF.LoadFromFile('supplier-invoice.pdf', '') <> 1 then
      raise Exception.Create('Could not load PDF');

    // Walks every Font dictionary; returns how many fonts
    // gained a font program. Fonts whose program cannot be
    // found on the system are skipped, not failed.
    Repaired := PDF.EmbedMissingFonts;
    Writeln(Format('%d font program(s) embedded', [Repaired]));

    PDF.SaveToFile('supplier-invoice-repaired.pdf');
  finally
    PDF.Free;
  end;
end;

Bilinmesi gereken bir ayrıntı, ad eşleştirmenin basit bir dize karşılaştırmasından neden daha iyi çalıştığını açıklar: kütüphane, BaseFont adlarını aramadan önce normalleştirir. Alt küme önekleri (altı büyük harf ve bir artı işaretinden oluşan ABCDEF+ deseni) kaldırılır, ArialMT gibi PostScript tarzı sonekler Arial olarak çözümlenir ve TrueType Koleksiyonu dosyaları algılanıp paketinden çıkarılır, böyle bir .ttc içinde yer alan bir yazı tipi yine de doğru şekilde gömülür

Onarımın bir ön uçuş raporu ile doğrulanması

CreatePreflightReport doğrulama adımıdır ve döngü kasıtlı olarak kapatılmıştır: dosyayı kusurlu bulan denetim, onu temizleyen denetimle aynı olmalıdır. 00030 hata kodu, "En az bir yazı tipi gömülü değil (FontFile/FontFile2/FontFile3 eksik)" şeklinde okunan PDF/A derin denetim bulgusudur ve dosyanın tamamı için rapor edilir, bu nedenle gözden kaçan tek bir yazı tipi bile hatayı canlı tutar. Raporu kaynak dosya üzerinde çalıştırın, onarın, kaydedin ve çıktı üzerinde tekrar çalıştırın

function HasFontEmbeddingViolation(PDF: TPDFlib;
  const FileName: string): Boolean;
var
  Report: string;
begin
  // ComplianceTests = 1 selects the PDF/A checks
  Report := PDF.CreatePreflightReport(FileName, '', 1, 0);
  Result := Pos('00030', Report) > 0;
end;

Dosya başına bir karar yerine yazı tipi başına bir görünüm için, onarılan belgeyi tekrar yükleyin ve listeleyin: FindFonts öğesini takip eden SelectFont ve GetFontIsEmbedded yazı tipi yazı tipi gömme durumunu bildirir; bu, bir toplu işin tam olarak hangi dosyadaki hangi yazı tipinin onarılamadığını günlüğe kaydetmesi gerektiğinde doğru araçtır. Aynı listeleme deseni, yüklenen PDF'lerden metin, resim ve yazı tiplerini ayıklama hakkındaki makalede de görünür; burada onarım yerine ayıklama işlemini besler

Yazı tipi sistemde yüklü olmadığında ne olur?

EmbedMissingFonts, programını bulamadığı her yazı tipini atlar ve atlama durumunu dönüş değeri aracılığıyla bildirir: eğer sayım, saydığınız gömülmemiş yazı tipi sayısından daha düşük gelirse, aradaki fark sistemin sahip olmadığı yazı tipleridir. Bu dürüst bir başarısızlık modudur ve alternatiflerden daha iyidir, çünkü belgede adı geçen bir yazı tipi için ikame bir program uydurmak işlemeyi değiştirir, ki bu arşivsel bir onarımın asla yapmaması gereken bir şeydir. Bu durumlar için losLab PDF Kütüphanesi EmbedFontProgramFromFile sunar; bu işlev arayan tarafından sağlanan bir .ttf veya .otf dosyasını belirtilen yazı tipine gömer, böylece bir iş hattı karşılaşmayı beklediği kurumsal yazı tiplerini taşıyabilir ve kasıtlı olarak bunlara geri dönebilir

var
  I, FontID: Integer;
begin
  PDF.FindFonts;
  for I := 1 to PDF.FontCount do
  begin
    FontID := PDF.GetFontID(I);
    if (FontID > 0) and (PDF.SelectFont(FontID) = 1) then
      if PDF.GetFontIsEmbedded = 0 then
        // Try the installed system font first, then fall back
        // to a font file shipped alongside the application
        if PDF.EmbedFontProgram(PDF.FontName) = 0 then
          PDF.EmbedFontProgramFromFile(PDF.FontName,
            'fonts\CorporateSans.ttf');
  end;
end;

İki sınırın açıkça belirtilmesi gerekir. İlk olarak, Type1 yazı tipleri mevcut uygulamada onarılmaz: bunların /FontFile girdisi, açık uzunluk anahtarlarına sahip üç bölümlü PFB yapısını gerektirir ve kütüphane, hatalı biçimlendirilmiş bir akış yazmak yerine bunları atlar; modern belgelerde nadirdirler ancak eski arşivlerde görünürler. İkinci olarak, bir yazı tipini gömmek bir lisanslama eylemidir. Bir TrueType yazı tipinin gömme izinleri dökümhanesine aittir ve lisanslı yazı tipi programlarını kuruluştan çıkan belgelere dolduran bir onarım iş hattı, yazı tipi lisanslarının buna gerçekten izin verdiğini birine onaylatmalıdır. Kütüphane istediğinizi yapacaktır; isteyip isteyemeyeceğiniz ise derleyicinizin değil, hukuk departmanınızın konusudur

Gömme gereklidir, ancak yeterli değildir

Yazı tiplerini onarmak yalnızca 00030 hatasını temizler, başka hiçbir şeyi değil. Şifreleme, eksik XMP meta verileri, OutputIntent içermeyen cihaza bağlı renk uzayı veya eksik ToUnicode eşlemeleri nedeniyle PDF/A doğrulamasından geçemeyen bir belge, her yazı tipi gömüldükten sonra da başarısız olmaya devam edecektir; bu nedenle onarım, bir ön uçuş odaklı döngünün içinde yer alır ve onun yerini almaz. Raporun tamamını çalıştırın, belirttiği hataları düzeltin ve raporun ne zaman bittiğinizi söylemesine izin verin. Bir de maliyet boyutu vardır: tam bir CJK yazı tipi programı megabaytlarca yer kaplar, bu nedenle bunlardan birkaçını gömmek küçük bir belgeyi önemli ölçüde şişirebilir. Karşı ağırlık ise, PDF dosya boyutu optimizasyonu ve yazı tipi alt kümeleme hakkındaki makalede ele alınan ve gömülü her programı belgenin gerçekten işlediği gliflere indirgeyen alt kümelemedir (subsetting)

Yeni belgelerin geriye gitmesini önlemek

SetEmbedAllFonts aynı özelliğin önleme yarısıdır: kendi kodunuzun bu makalede onarılan belgeleri üretmesini durduran bir yazıcı tarafı korumasıdır. SetEmbedAllFonts(1) aktifken, Embed=0 talep eden sonraki herhangi bir AddTrueTypeFont çağrısı, PDF/A modunun halihazırda uyguladığı garantiyi her belgeye genişleten gömülü bir referansa yükseltilir. Yüklenmiş bir dosyadaki yazı tiplerini değil, çağrıdan sonra eklenen yazı tiplerini etkiler, bu nedenle iş bölümü nettir: oluşturduğunuz belgeler için SetEmbedAllFonts, devraldığınız belgeler için EmbedMissingFonts

PDF.NewDocument;
PDF.SetEmbedAllFonts(1);
// From here on, AddTrueTypeFont(Name, 0) behaves
// like AddTrueTypeFont(Name, 1): no non-embedded
// reference can reach the output file

Her iki yarı da, yani yazıcı tarafı koruması ve yükle-onar-kaydet yolu, sonucu doğrulayan ön uçuş motoruyla birlikte Delphi, C# ve VB.NET için losLab PDF Library'nin bir parçasıdır; ürün sayfası, yazı tipi başına gömme ve alt kümeleme çağrılarını içeren tam yazı tipi API referansını taşır