Teknik Makale

PDFlibPas: CJK ve Emoji İçin Otomatik Yazı Tipi Yedekleme

PDFlibPas, seçili yazı tipinin çizemediği karakterleri, kurulu yüzlerden oluşan bir yedekleme zincirinde küme küme arayarak, şekillendirmeyi ve çift yönlü metin sırasını koruyarak çözer. Bunu SetAutomaticFontFallback ile etkinleştirir, zinciri AddFontFallback ile genişletirsiniz ve çıktı için gerçekten kullanılan yedek yazı tipleri dosyaya gömülür

Çözdüğü sorun, her belge üretecinin, bir müşteri adı şablon yazı tipinin hiç öngörmediği bir yazı sisteminde geldiğinde ilk kez karşılaştığı sorundur. Hata sessizdir ve onu pahalı kılan da budur

Desteklenmeyen metin neden hata vermek yerine kayboluyor?

Çünkü PDF'te, bir karakteri çizemeyen yazı tipi diye bir kavram yoktur. Basit bir yazı tipi, bayt kodlarını bir kodlama üzerinden glif adlarına eşler; bileşik bir yazı tipi ise kodları bir CMap üzerinden glif indekslerine eşler. Yüzün içermediği bir glif istediğinizde sıfır glif indeksini, .notdef'i alırsınız ve çoğu yüz bunu hiçbir şey çizmeyerek ya da boş bir kutu olarak gösterir. Dosya yapısal olarak geçerlidir, metin operatörü doğru biçimlendirilmiştir ve sayfa oluşturulur. Sadece adın olması gereken yer boştur

ISO 32000-1'de bir üreticinin bunu fark etmesini gerektiren hiçbir şey yoktur. Kapsamı kontrol etmeden metin yazan bir üretici, sessizce içerik kaybetmiş ama teknik olarak uyumlu bir PDF üretir ve bu kayıp haftalar sonra müşterinin ekranında ortaya çıkar. Yedekleme özelliği ile eksik glif raporunun birlikte gelmesinin nedeni budur: çözülebileni çözmek işin yalnızca yarısıdır, çözülemeyeni raporlamak ise diğer yarısıdır

Yedekleme kod noktası başına değil, küme başına gerçekleşir

Ayrıntı düzeyi, çalışan bir uygulamayı yalnızca inandırıcı görünen bir uygulamadan ayıran ayrıntıdır. Metin, bağımsız karakterlerin bir dizisi değildir. Bir Devanagari hecesi, ten tonu değiştiricili bir emoji, birleştirme işaretli bir temel harf: her biri tek bir yazı tipiyle oluşturulması gereken tek bir kümedir, çünkü içindeki şekillendirme kararları o yüzün tablolarına bağlıdır

PDFlibPas kümeleri çözer, bu yüzden bir yedek yüzün kapsadığı küme tamamen o yüz tarafından çizilir. Küme ortasından bölüp yarısını birincil yazı tipinden yarısını da bir yedekten çizmek, teknik olarak mevcut ama görsel olarak bozuk bir sonuç üretirdi ki bu, başlangıçtaki boşluktan muhtemelen daha kötüdür. Metin sırası da korunur, bu yüzden sağdan sola bir akış içindeki bir yedekleme çevresindeki metnin sırasını bozmaz; aynı mekanizma Japonca ve Çince için dikey yazım'da anlatılan dikey düzenin de temelini oluşturur

var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);
    Lib.SetAutomaticFontFallback(1);

    // Arama sırası: ilk eşleşen kazanır, bu yüzden en geniş kapsamlı yüzleri en sona koyun
    Lib.AddFontFallback('Microsoft YaHei');   // Basitleştirilmiş Çince
    Lib.AddFontFallback('Meiryo');            // Japonca
    Lib.AddFontFallback('Segoe UI Symbol');
    Lib.AddFontFallback('Segoe UI Emoji');

    Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_REPORT);

    Lib.AddTrueTypeFont('Arial', 1);          // 1 = yüzü göm
    Lib.SetTextSize(11);
    Lib.DrawText(72, 720, 'Invoice for 北京示例科技有限公司');
    Lib.DrawText(72, 700, 'Delivery status: on time');

    Lib.SaveToFile('invoice.pdf');
  finally
    Lib.Free;
  end;
end;

Zinciri bilinçli olarak sıralayın. Çözümleme, kümeyi kapsayan ilk yüzü alır, bu yüzden en başa konan geniş kapsamlı bir pan-Unicode yazı tipi neredeyse her şeyi kazanır ve özenle seçtiğiniz yazı sistemine özgü yüzlere hiç başvurulmaz. Belirli yüzleri başa, genel amaçlı olanı ise sona koyun

Raporla mı, iptal mi: hangi başarısızlığı istersiniz?

SetMissingGlyphPolicy, uyumlu varsayılan değer olan PDF_MISSING_GLYPH_REPORT'u ya da PDF_MISSING_GLYPH_ABORT'u alır. Rapor politikasında metin işlemi devam eder, çözülemeyen kod noktaları öncekiyle aynı şekilde atlanır ve her biri kaydedilir. İptal politikasında ise metin işlemi hiçbir içerik yazılmadan reddedilir ve LastErrorCode 521 olarak ayarlanır

Seçiminizi belgenin amacına göre yapın. Bir grup dahili rapor oluşturmaya devam etmeli ve boşlukları günlüğe kaydetmelidir, çünkü bugün biraz eksik bir rapor, hiç rapor olmamasından daha iyidir. Yasal bağlayıcılığı olan bir sözleşme, bir fatura ya da üzerinde bir ad bulunan herhangi bir şey iptal edilmelidir, çünkü bir taraf adındaki sessizce düşürülmüş bir karakter, bir anlaşmazlıkta değil kendi sürecinizde keşfetmek isteyeceğiniz bir kusurdur. İptal politikası yazmadan önce başarısız olur, bu yüzden geride yarım kalmış bir içerik akışı bırakmaz

var
  Lib: TPDFlib;
  Report: WideString;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_ABORT);
    // ... belgeyi oluştur ...

    if Lib.DrawText(72, 660, CustomerName) <> 1 then
      if Lib.LastErrorCode = PDFLIB_ERROR_MISSING_GLYPH then
      begin
        Report := Lib.GetMissingGlyphReportJSON;
        // {"valid":false,"policy":1,"eventCount":1,"events":[
        //   {"sequence":1,"documentIndex":0,"page":1,"utf16Index":12,
        //    "codePoint":21271,"unicode":"U+5317","fontName":"Arial",
        //    "fontType":"TrueType","operation":"DrawText"}]}
        EscalateToOperator(Report);
      end;
  finally
    Lib.Free;
  end;
end;

Rapor, bilinçli olarak makine tarafından okunabilir ve sınırlı tutulmuştur. Her olay sayfayı, dize içindeki UTF-16 indeksini, kod noktasını hem sayısal hem U+XXXX biçiminde, seçilen yazı tipini, türünü ve sorunu yaşayan işlemi taşır; böylece bir destek talebi bir belirtiyi anlatmak yerine tam karakteri adlandırabilir. İzleyici en son 256 olayı tutar; bu, bir belgeyi teşhis etmeye yeter ve patolojik bir çalıştırmanın tanılamayı bir bellek sorununa çevirmesine izin vermeyecek kadar küçüktür

Ölçüm ve çizim birbiriyle uyuşmalıdır

Genişlik ölçümü, çizimle aynı küme farkındalıklı yedekleme kararlarını kullanır. Bu kulağa açık gelir ama çoğu ev yapımı yedekleme katmanının yanlış yaptığı şey tam olarak budur: çizim yolunu yamalar, ölçümü birincil yazı tipinde bırakırlar ve her metin kutusu, sağa hizalama ve tablo sütunu, oluşturulanla uyuşmayan genişliklerden hesaplanmış olur

Her iki yol da aynı çözümlemeyi paylaştığından, çizimden önce ölçülen bir dize, yedekleme akışları dahil olmak üzere ölçüldüğü genişliği kaplar. Yedeklemeyi yalnızca elle denetlediğiniz yerlerde değil, genel olarak etkinleştirmeyi güvenli kılan da budur

Yalnızca kullandığınız gömülür

Yedek yazı tipleri tembel bir şekilde gömülür: zincirde hiçbir kümeyi çözmemiş bir yüz, çıktıya hiçbir katkı sağlamaz. Bir Çince karakter ve 5.000 Latin karakter içeren bir belge, tam bir CJK yüzü taşımaz; yalnızca o tek glif için alt kümeleme geçişinin ürettiğini taşır ki bu, dosya boyutu optimizasyonu ve yazı tipi alt kümeleme'de anlatılan davranıştır

Bu tembellik, geniş bir zinciri yapılandırmayı ucuza getirir. Hizmet verdiğiniz her yerel ayarda belge setinizin ihtiyaç duyabileceği yüzleri kaydedin; her bir PDF yalnızca gerçekten kullandığının bedelini öder. Sizin üretmediğiniz, eksik yüzlerin zaten mevcut bir dosyanın içinde olduğu belgeler için onarım yolu farklıdır ve mevcut bir PDF'e eksik yazı tiplerini gömme'de ele alınmıştır

Belirtilmesi gereken bir dağıtım uyarısı var: yedekleme, kodu çalıştıran makinede kurulu yüzlere göre çözümlenir. CJK yazı tipleri kurulu olmayan bir sunucunun geri düşebileceği hiçbir şey yoktur ve rapor bunu ilk şikayetten sonra değil, ilk belgede size söyler. Bağımlı olduğunuz yazı tiplerini birlikte dağıtın ve bunları gömmek için lisanslamayı doğrulayın

PDFlibPas, eşleşen DLL ve ActiveX arayüzlerine sahip bir Delphi, C++Builder ve Lazarus PDF kütüphanesidir; bu yüzden yedekleme ve eksik glif API'leri Pascal dışındaki çağıranlar için de kullanılabilir. Tam belgeler PDFlibPas Delphi PDF kütüphane sayfasında bulunur