Teknik Makale

PDF Yazı Tipleri ve Metin: Neden Glifler Kutulara Dönüşür?

Makinenizde mükemmel görünen ve başka birininkinde bir dizi boş kutu olarak oluşturulan bir PDF, belge yazılımındaki en yaygın yazı tipi (font) kusurudur ve bu neredeyse hiçbir zaman metnin yanlış olduğu anlamına gelmez. Karakterler bozulmamıştır, kodlama iyidir, glifler basitçe orada değildir. İki makine arasında değişen şey, işletim sisteminin hangi yazı tiplerini yüklediğidir ve taşınabilir bir dosya ile kırılgan bir dosya arasındaki uçurum, sayfa yazılırken verilen bir karardır: yazı tipinin PDF'in içinde seyahat edip etmediği veya diğer uçta var olduğunun varsayılıp varsayılmadığı

Bunun neden olduğunu ve neden ayrı bir başarısızlığın anlamsız (gibberish) olarak kopyalanan aranabilir görünümlü metin ürettiğini anlamak, PDF'in metni nasıl sakladığına bakmak anlamına gelir. Cümleleri saklamaz. Glif kodlarını artı bir yazı tipi programını artı birini diğerine eşleyen tabloları saklar ve her oluşturma (rendering) veya çıkarma (extraction) hatası bu üçü arasındaki bir boşlukta yaşar. Aşağıdakiler, önemli oldukları yerlerde onu kontrol eden Delphi çağrılarıyla birlikte, ISO 32000'e dayanan bu mekanizmanın bir turudur

Karakterler, kodlar ve glifler üç farklı şeydir

Kelime dağarcığı insanları çelmeletir çünkü günlük konuşma üç farklı fikri "harf" (letter) kelimesinde birleştirir (collapses). Bir karakter, soyut bir yazma birimidir, Unicode'da U+0041 olarak tanımlanan büyük A fikridir. Bir glif, belirli bir yazı tipinin bu karakteri tasvir etmek (depict) için kullandığı eğri ve gövde anahattı (curve-and-stem outline) olan çizilmiş bir şekildir. Aralarında kod oturur: içerik akışındaki, görüntüleyiciye mevcut yazı tipinde hangi glifi boyayacağını söyleyen bayt veya baytlar

PDF kodlarla çalışır. Bir içerik akışı bir dize (string) gösterdiğinde, bu baytlar Unicode değil, etkin yazı tipine yönelik dizinlerdir (indices). Yazı tipinin kodlaması (encoding), 65 kodunun "65'in altındaki glifi çiz" anlamına geldiğine karar verir ve bu işlemdeki hiçbir şey sonucun bir insana A gibi göründüğünü bilmez. PDF'in glifleri bulabildiği her yerde aynı şekilde oluşturulmasını (render) sağlayan şey budur ve aynı zamanda çıkarmanın ekrandan (display) ayrı bir sorun olmasının nedeni de budur: çizimin yalnızca koddan glife ihtiyacı vardır, okumanın koddan Unicode'a ihtiyacı vardır ve bunlar aynı fikirde olmayabilen veya bağımsız olarak kaybolabilen iki farklı tablodur

Gerçekte karşılaşacağınız yazı tipi türleri

ISO 32000 çeşitli yazı tipi sözlüğü türlerini tanımlar ve pratikte aldığınız veya oluşturduğunuz bir belge üçünden birini kullanır. Hangisine baktığınızı bilmek, yanlış gidebilecek çoğu şeyi açıklar

Type 1, kübik Bezier eğrilerinden oluşturulmuş Adobe'nin orijinal PostScript anahat biçimidir. Uyumlu (conforming) her okuyucunun sağlaması gereken on dört standart yazı tipi olan Helvetica, Times, Courier, Symbol ve ZapfDingbats aileleri Type 1'dir ve bunlardan birini adlandıran bir yazı tipi sözlüğü, yasal olarak yazı tipi programını atlayabilir. Bu, bir yazı tipini gömülmemiş (unembedded) bırakmanın şanstan ziyade belirtim (specification) gereği güvenli olduğu tek durumdur. Başka herhangi bir Type 1 yüzey (face) için program gömülmelidir, aksi takdirde görüntüleyici bir şeyi, genellikle metrik olarak benzer ancak görünürde farklı bir yazı tipini ikame eder (substitutes)

TrueType, kuadratik eğriler kullanır ve Apple ve Microsoft dünyasından gelmiştir. Çoğu sistem yazı tipinin olduğu şeydir ve en sık gömeceğiniz şeydir. PDF'deki basit bir TrueType yazı tipi tek baytlık kodlarla sınırlıdır, bu nedenle böyle bir yazı tipi aynı anda en fazla 256 glifi adresleyebilir. Bu üst sınır (cap), CJK ve diğer büyük komut dosyalarının (scripts) basit bir yazı tipine binememesinin yapısal nedenidir

Type 0, bileşik veya CID anahtarlı (CID-keyed) yazı tipi, bu sınıra verilen cevaptır. Onları anahatları TrueType veya CFF/Type 1 olan bir alt (descendant) CIDFont üzerinden yönlendirmek için çok baytlı kodlar ve bir CMap kullanır. Bu, binlerce glifi taşıyabilen tek yazı tipi türüdür, bu nedenle yazar düşünse de düşünmese de Çince, Japonca, Korece veya geniş bir çok dilli karışım (multilingual mix) barındıran herhangi bir PDF Type 0 kullanıyordur. Takas karmaşıklıktır (complexity): hem oluşturma (rendering) hem de çıkarma (extraction) için doğru olması gereken daha fazla hareketli parça

PDF'de 12, 18, 24 ve 36 punto olarak oluşturulmuş bir TrueType yazı tipi, tek bir gömülü anahattın herhangi bir boyuta ölçeklendiğini gösteriyor

Bu resmin arkasındaki bir ayrıntı dosya boyutunu yönlendirir. Bir yazı tipi sabit boyutlu bit eşlemler (bitmaps) değil, bir anahatlar kitaplığıdır, bu nedenle aynı gömülü program sayfadaki her punto boyutuna hizmet eder. Ölçekleme (scaling), çizim zamanında uygulanan bir dönüşümdür (transform), bir başlığın ve gövde metninin tek bir gömülü yüzeyi (face) paylaşmasının ve gömme maliyetinin boyut başına değil, yazı tipi başına olmasının nedeni budur

Gömme (Embedding), taşınabilir ile kırılgan arasındaki farktır

Gömme, yazı tipi programının, yani gerçek anahat verilerinin (outline data), bir akış (stream) olarak PDF'e yazılması anlamına gelir. Yazı tipinizi hiç duymamış bir makinedeki okuyucu, bu anahatları doğrudan dosyadan okur ve tam glifleri çizer. Gömmeyi atlarsanız (skip), hedefin aynı isimde bir yazı tipine sahip olduğuna bahse giriyorsunuz demektir; olmadığında görüntüleyici bir ikameye (substitute) geri döner (falls back). Standart on dörtlü için bu ikame tanımlanmıştır ve iyi huyludur (benign). Diğer her şey için, farklı bir yazı biçiminde (typeface) kıl payı kurtulmadan (near-miss), betiği (script) kapsayan hiçbir ikame olmadığındaki boş kutu (empty-box) sonucuna kadar değişir

HotPDF ile kontrol tek bir özelliktir (property), belge açılmadan önce ayarlanır. FontEmbedding, kütüphaneye çizdiği yüzeyleri (faces) dosyanın içine paketlemesini söyler:

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'report.pdf';
    Pdf.Compression := cmFlateDecode;
    Pdf.FontEmbedding := True;          // outlines travel inside the file
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Calibri', [], 11);
    Pdf.CurrentPage.TextOut(72, 760, 0, 'This renders the same on a machine without Calibri.');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Sıralama kozmetik (cosmetic) değildir. BeginDoc, HotPDF'in belge yapısını taahhüt ettiği (commits) yerdir, bu nedenle FontEmbedding bu çağrıdan önce doğru olmalıdır. Daha sonra atayın ve hiçbir hata, hiçbir uyarı olmaz, sadece sessizce yazı tipleri olmadan dışarı çıkan bir dosya olur. Bu en kötü hata türüdür: yazı tipinin yüklü olduğu geliştiricinin makinesindeki her testi geçer ve yalnızca yüklü olmadığı müşterinin makinesinde ortaya çıkar (surfaces)

Gömme (embedding) aynı zamanda lisanslamanın mühendislikle buluştuğu yerdir. Bir yazı tipi programı, serbestçe, yalnızca önizleme için gömülüp gömülemeyeceğini veya hiç gömülemeyeceğini açıklayan bayraklar (flags) taşır. Bu bayrakları onurlandırmak sizin sorumluluğunuzdadır, oluşturucunun (renderer's) değil ve "çalıştı" ile "izin verildi" aynı şey değildir

Alt kümeye ayırma (Subsetting): yalnızca kullandığınız glifleri gömün

Tam gömme, yazı tipi programının tamamını dosyaya yazar. Büyük bir CJK TrueType yüzeyi birkaç megabayta kadar çıkabilir ve bir düzine karakteri göstermek için tamamını gömmek, çok sayfalı bir belge boyunca bileşikleşecek (compounds) şekilde israftır. Alt kümeye ayırma, yalnızca belgenin başvurduğu (references) glifleri yazarak, ardından yazı tipini altı harfli bir etiket (tag) ve bir artı işaretiyle (herhangi bir alt kümeye ayrılmış PDF'nin yazı tipi listesindeki ABCDEF+Calibri formu) yeniden adlandırarak bunu çözer, böylece okuyucu kısmi (partial) yüzeyi aynı isimli tam bir sistem yazı tipiyle asla karıştırmaz

Oluşturulan çoğu belge için alt kümeye ayırma doğru varsayılandır (default). Dosya boyutunu kaynak yazı tipine değil içeriğe orantılı tutar; bu, aksi takdirde dosyaya hakim olacak büyük çok dilli yazı tipleri için en önemli şeydir. Tek uyarı, bir alt kümenin (subset) yalnızca oluşturma anında kullanılanları içermesidir. Bir aşağı akış (downstream) süreci daha sonra alt kümeye ayrılmış bir yazı tipine metin eklemeye çalışırsa, ihtiyaç duyduğu glifler dosyada bulunmayabilir, bu da başkasının PDF'inin artımlı (incremental) düzenlenmesi üzerinde gerçek bir kısıtlamadır (constraint)

Unicode yazı tipleri ve CJK kutusu sorunu

Metin düz (plain) Latin olmadığında basit yazı tipi (simple-font) yolu tükenir ve çözüm Unicode yeteneğine sahip bir yazı tipini açıkça (explicitly) kaydetmek ve HotPDF'in ondan bir Type 0 yazı tipi oluşturmasına izin vermektir. RegisterUnicodeTTF, yola göre (by path) bir TrueType dosyası yükler; bundan sonra kaydedilen isim diğerleri gibi SetFont içinde kullanılabilir:

Pdf.FontEmbedding := True;
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansCJKsc-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('NotoSansCJKsc-Regular', [], 14);
Pdf.CurrentPage.TextOut(72, 720, 0, '你好,世界 こんにちは 안녕하세요');
Pdf.EndDoc;

İki şey bunu yapar ya da bozar. Yazı tipi dizedeki (string) komut dosyalarını (scripts) kapsamalıdır: yalnızca Latince (Latin-only) bir TrueType, siz istediğiniz için Çince glifler büyütmez ve sonuç yine boş kutulardır, bu sefer çünkü glif gerçekten (genuinely) o yüzeyde mevcut değildir. Ve gömme açık kalmalıdır, çünkü kayıtlı (registered) bir TTF'den oluşturulan (assembled) Type 0 bir yazı tipi, anahatları (outlines) bulamayan bir okuyucu için anlamsızdır. Karma (mixed) içerik için dayanıklı seçim, genellikle gömülü ve alt kümeye ayrılmış olan Noto ve Arial Unicode MS aileleri gibi geniş kapsamlı (broad-coverage) bir yüzeydir (face)

Sağdan sola ve karmaşık (complex) betikler, kapsamanın (coverage) üzerine bir şekillendirme (shaping) katmanı ekler. HotPDF, mantıksal düzeni geçirmeniz ve kitaplığın onu düzenlemesine izin vermeniz için yönsel (directional) yeniden sıralamayı işleyen Arapça ve İbranice için RtLTextOut sunar (exposes). Arapçayı doğru yapmak kapsam artı şekillendirme artı yöndür, üç ayrı şeydir ve oradaki bir kutu bunlardan herhangi birinin başarısız olduğu anlamına gelebilir

ToUnicode tablosu: kopyala-yapıştır'ın yaşadığı yer

Yukarıdaki her şey çizimle ilgilidir. Çıkarma (extraction) ayna görüntüsüdür ve kendi nedenlerinden dolayı başarısız olur. Bir görüntüleyici, yazı tipinin koddan glife (code-to-glyph) eşlemesini kullanarak bir sayfayı oluşturur, ancak bir kullanıcı metni seçip kopyaladığında, görüntüleyicinin aynı kodları tekrar Unicode'a dönüştürmesi gerekir. Bu ters (reverse) eşleme, yazı tipine eklenmiş isteğe bağlı bir akış olan ToUnicode CMap'tir

Var olduğunda ve doğru olduğunda, kopyalanan metin doğru karakterler olarak çıkar. Olmadığında veya yanlış olduğunda ya da yazı tipi özel glif kodlarıyla alt kümeye ayrıldığında ve ToUnicode yazılmadığında, sayfa mükemmel görünür ve pano çöple (garbage) dolar: glif kodları Unicode'muş gibi okunur ki özel kodlanmış bir alt küme (custom-encoded subset) için öyle değildirler. OCR metin katmanına sahip taranmış bir belgenin aranabilir olmasının (searchable) ancak dikkatsiz bir oluşturucudan (generator) gelen dijital doğumlu (born-digital) bir PDF'in olmamasının nedeni budur. Oluşturma (rendering) ve çıkarma (extraction) farklı tablolardan yararlanır, bu nedenle bir dosya birini tatmin edebilir ve diğerinde başarısız olabilir. Çıkarma çıktınız için önemliyse, doğru bir ToUnicode haritasını (map) bir gereklilik (requirement) olarak ele alın ve orada olduğuna güvenmek (trusting) yerine bir örnekten metin kopyalayarak doğrulayın

Bir yazı tipi hatası (bug) nasıl hızlı bir şekilde teşhis edilir

Başarısızlık modu size nereye bakacağınızı söyler. Başka bir makinedeki boş kutular neredeyse her zaman gömülmemiş bir yazı tipi anlamına gelir, bu nedenle önce gömmeyi (embedding) ve ikinci olarak glif kapsamını (coverage) kontrol edin. Kendi makinenizde bile görünen kutular (boxes) kapsamayı işaret eder: yazı tipi, gömmeden (embedding) bağımsız olarak bu komut dosyasını (script) içermez. Doğru bir şekilde oluşturulan ancak saçma (nonsense) olarak kopyalanan metin bir ToUnicode sorunudur, bir oluşturma (rendering) sorunu değildir ve çizim asla bozulmadığı için yazı tipleri veya gömme ile oynamak (fiddling) onu düzeltmez. Bitmiş bir dosyayı okumak için Acrobat'ta açın ve Belge Özellikleri, Yazı Tipleri'ne (Document Properties, Fonts) bakın: sağlıklı bir giriş (entry) türü gösterir, Embedded (Gömülü) veya Embedded Subset (Gömülü Alt Küme) der ve kodlamayı (encoding) adlandırır. Gömülmesi gereken ve olmayan bir yazı tipi, bir müşteriden önce kendini orada duyurur

Karakter, kod ve glif arasındaki bölünme netleştiğinde (clear) bunların hiçbiri egzotik (exotic) değildir. Çizim yaptığınız yazı tiplerini gömün (embed), büyük olanları alt kümeye ayırın (subset), metin Latince'den ayrıldığı an Unicode bir yüzeye (face) ve RegisterUnicodeTTF'ye ulaşın ve herhangi biri metni çıkaracaksa doğru bir ToUnicode haritasını tutun. Bunları doğru yaparsanız kutuların görünmesi durur. Çevredeki mekanikler için, minimal bir PDF'in anatomisi (anatomy), yazı tipi sözlüğünün (font dictionary) nesne ağacında (object tree) nerede oturduğunu gösterir ve belge yapısı incelemesi (walkthrough), kaynakların (resources) sayfalar arasında nasıl paylaşıldığını kapsar

Burada gösterilen SetFont, FontEmbedding ve RegisterUnicodeTTF çağrıları, Delphi ve C++Builder için HotPDF Bileşeninin (Component) bir parçasıdır