Bir rapor oluşturun, bir TrueType yazı tipi gömün; çıktı denediğiniz her görüntüleyicide doğru şekilde açılır. Karakterler doğrudur, metin seçilebilirdir, dosya geçerlidir. Yanlış olan tek şey boyuttur. Birkaç düzine Latin karakteri kullanan bir belge, 350 KB'lık yazı tipinin tamamını taşır. Çince bir paragraf yazdıran bir belge, ihtiyaç duyması gereken yarım megabaytlık dilim yerine 14 MB'lık bir CJK yazı tipini taşır. Hiçbir istisna oluşturulmadı, hiçbir uyarı günlüğe kaydedilmedi ve dosya doğrulamadan geçti. Bu, dışarıdan bakıldığında yanlış sıralanmış bir sonlandırma adımının nasıl göründüğüdür: hiçbir şey başarısız olmaz ve tek kanıt çok büyük bir sayıdır
Bunu bir ders olarak yazmaya değer, çünkü hatanın biçimi geneldir. Her belge motorunda, nesneleri yazmadan hemen önce değiştiren bir sonlandırma aşaması vardır ve o aşamanın doğruluğu, adımlarının serileştirmeye göre sırasına tümüyle bağlıdır. Tek bir adımı yazmanın yanlış tarafına alırsanız hiçbir şey yapmaz — sessizce
Yazı tipi alt kümelemenin ne yapması gerekir
Bir alt küme yazı tipi, bir belgenin gerçekten kullandığı bir TrueType dosyasının parçasıdır. ISO 32000-1 §9.9, gömülü bir yazı tipi programının yazı tipi tanımlayıcısı tarafından başvurulan bir akışta nasıl yer aldığını açıklar ve bir TrueType programı için bu akış, sıkıştırılmamış bayt sayısını veren bir /Length1 ile birlikte /FontFile2'dir. Alt kümeleme, glyf ve loca tablolarını yeniden yazarak yalnızca belgenin başvurduğu glifleri içermelerini sağlar, glif tanımlayıcılarını yeniden numaralandırır ve yazı tipini bir alt küme olarak işaretlemek için spesifikasyonun gerektirdiği şekilde /BaseFont adının önüne ABCDEF+ gibi altı harfli bir etiket ekler. On veya on beş kilobayta düşürülen Latin yazı tipi, yalın bir PDF ile tek bir başlık uğruna tüm bir yazı tipini gönderen bir PDF arasındaki farktır
Bunun gerçekleştiği nokta önemlidir. Alt kümeleme, diskte zaten bulunan baytlara uyguladığınız bir dönüştürme değildir. Bellekteki nesne grafiğini düzenler: /FontFile2 akış içeriğini küçültür, /Length1 değerini düzeltir ve /BaseFont dizesini yeniden yazar. Serileştirici grafiği dolaşırken ve baytları yayarken tüm bunların yerinde olması gerekir. Düzenlemeler baytlar yazıldıktan sonra gerçekleşirse, hiç kimsenin okumayacağı nesneleri güncellerler
Belirti ve neden hiçbir şeyin şikayet etmediği
Alt kümeleme yazmanın yanlış tarafına düştüğünde belirti, herhangi bir teşhis olmaksızın çıktıda tam yazı tipleridir. Bir Unicode TrueType yazı tipi kaydedin, normal bir belge üretin; gömülü yazı tipi nesnesi kaynak .ttf dosyasıyla aynı uzunlukta çıkar ve /BaseFont adında altı harfli alt küme öneki yoktur. On glif kullanan çalıştırmalarla on bin glif kullanan çalıştırmalar arasında çıktı hiç küçülmez
Herhangi bir hatanın olmaması, bu hata sınıfını pahalı kılan kısımdır. Yanlış zamanda çalışan bir alt kümeleme yordamı yine de çalışır. Birikmiş kod noktası kullanımını gezer, tümüyle doğru bir alt küme oluşturur ve bellekteki nesne grafiğine uygular. İçeride iş bitmiştir ve çağrı temiz döner. Yanlış olan tek şey, düzenlediği nesne grafiğinin artık yazılan şey olmamasıdır, çünkü yazıcı işini çoktan bitirmiştir. Çağıranın gözünden belge sorunsuz üretilip kaydedilmiştir; sessiz bir başarısızlığın verdiği izlenim tam olarak da budur
Alt kümeleme, sonlandırma sırasında nerede durur
HotPDF'te kapatma işlemi EndDoc içinde gerçekleşir. Alt kümeleme adımı, BuildAndApplyUnicodeFontSubset adında dahili bir rutindir. Metin yayma yolunun glifler gösterildikçe doldurduğu bir bit eşleminde tutulan belge başına kullanılan kod noktaları kümesini okur, kullanılan her kod noktasını önbelleğe alınmış kod noktasından glife tablosu aracılığıyla gerçek bir glif tanımlayıcısına eşler ve yazı tipi programını bu kapsam çevresinde yeniden yazar. Bir Unicode TrueType yazı tipi kaydedildiğinde, yayma yolu çizdiği her karakter için kullanılan kod noktaları kümesinde bir bit ayarlar, böylece belge kapandığında motor alt kümenin hangi glifleri tutması gerektiğini tam olarak bilir
Tuzak, o çağrının konumudur. BuildAndApplyUnicodeFontSubset, SaveToStream ya da SaveToFile belgeyi çoktan serileştirdikten sonra çağrılırsa, alt kümeleyicinin /FontFile2 üzerindeki düzenlemeleri, düzelttiği /Length1 ve altı harfli /BaseFont öneki, baytlara çoktan dönüştürülmüş bir nesne grafiğine göre hesaplanır. Doğru sıra önce alt kümeleyiciyi çalıştırır, serileştirme sonra gelir; böylece yazıcı özgün fontu değil alt kümelemiş fontu yazar. Doğru yapmak tek bir çağrının konumu meselesidir, fazlası değildir
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansSC-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Noto Sans SC', [], 12);
Pdf.CurrentPage.TextOut(72, 760, 0, '报表标题 Report Heading');
Pdf.EndDoc; // alt kümeleme burada yazmadan önce çalışır
Pdf.SaveToFile('Report.pdf');
finally
Pdf.Free;
end;
end;
Bunların hiçbiri çağıran kodda görünmez. Bir Unicode TrueType font kaydedildikten sonra alt kümeleme varsayılan olarak açıktır. Fontu kaydeder, belgeyi başlatır, çizim yapar ve bitirirsiniz; alt küme, baytlar bellekten çıkmadan önce kullandığınız gliflerden oluşturulur
Yanlış yerleştirilmiş tek bir adım neden bütün bir hata sınıfıdır
Bunun bir dipnot yerine bir ders niteliğinde olmasının nedeni, EndDoc işlevinin bir dizi kapatma adımı yayması ve bunlardan her birinin yazma işlemine göre konumuna duyarlı olmasıdır. Yazı tipi alt kümeleme bunlardan biridir. PDF/A çıktısı, alt kümede bulunan glif tanımlayıcılarını tam olarak listeleyen bir /CIDSet akışı gerektirir; bu, bir doğrulayıcının gömülü programın yazı tipi tanımlayıcısının iddia ettiği şeyle eşleştiğini onaylayabilmesi için ISO 19005'in getirdiği bir kısıtlamadır; bu akış aynı sonlandırma penceresinde yayılır ve önce alt kümenin oluşturulmuş olmasına bağlıdır. PDF/UA-1, ISO 14289-1 §7.18.3 uyarınca, açıklama taşıyan her sayfanın /S değerine sahip /Tabs bildirmesini gerektirir ve EnsurePDFUATabsOnAnnotatedPages adlı dahili bir rutin aynı aşamada bu anahtarı damgalar. Çıktı amacı kontrolleri de burada çalışır
Alt kümelemeyi devre dışı bırakan aynı sıralama hatası, açıklamalı sayfalarda PDF/UA sekme sırası anahtarının düşmesine de yol açardı, çünkü o adım aynı pencerede durur. veraPDF ve PAC, eksik bir /Tabs /Syi Matterhorn protokolü 21-001 kontrol maddesinin ihlali olarak raporlar. Yani yanlış yerleştirilmiş tek bir çağrı yalnızca dosya boyutunu şişirmez; aynı zamanda erişilebilirlik uyumluluk gereksinimini de sessizce bozar ve yine aynı hatasızlıkla. Sonlandırma aşamasının tehlikesi budur: adımları ortak bir ön koşulu paylaşır ve tek bir sıralama hatası, her çağrı başarı döndürürken birkaçını birden devre dışı bırakabilir
Sessiz bir yayma hatası gerçekte nasıl yakalanır
İstisna fırlatmayan bir başarısızlık, program çalıştırılarak yakalanmaz. Çıktının incelenmesi ve girdinin ne üretmesi gerekiyorsa onunla karşılaştırılmasıyla yakalanır. Font alt kümeleme için denetimler somuttur. Çıktı dosyası boyutunu kaba bir beklentiyle karşılaştırın: birkaç glife dokunan bir belge, tam bir yazı tipi boyutunda olmamalıdır. Gömülü font nesnesini açın ve bayt uzunluğunu okuyun; Latin bir yüz için alt kümelemiş bir /FontFile2, kaynak dosyanın küçük bir kesridir. /BaseFont adını okuyun ve altı harfli önekin orada olduğunu teyit edin; yokluğu, hiç alt küme uygulanmadığının doğrudan sinyalidir
var
Pdf: THotPDF;
Output: TMemoryStream;
begin
Output := TMemoryStream.Create;
try
Pdf := THotPDF.Create(nil);
try
Pdf.RegisterUnicodeTTF('C:\Fonts\DejaVuSans.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('DejaVu Sans', [], 11);
Pdf.CurrentPage.TextOut(72, 760, 0, 'Subset me');
Pdf.EndDoc;
Pdf.SaveToStream(Output);
finally
Pdf.Free;
end;
// ~700 KB'lik bir yüzden birkaç glif, yüzlerce KB'lik bir akış üretmemeli
if Output.Size > 100 * 1024 then
raise Exception.Create('Font subset did not shrink the output');
finally
Output.Free;
end;
end;
PDF/A çıktısı için kontrol daha da keskindir, çünkü bir doğrulayıcı işi sizin yerinize yapar. Uyumluluk seviyesini ayarlayın ve sonucu veraPDF aracılığıyla çalıştırın: eksik bir /CIDSet veya tanımlayıcıyla eşleşmeyen bir alt küme, gözle fark etmeniz için bırakılmak yerine başarısız bir madde olarak bildirilir. Bu sonlandırma çalışmasını yönlendiren uyumluluk anahtarları belgedeki özelliklerdir. PDFACompliance, PDF/A-2 Seviye B için '2B' gibi bir dize alır ve PDFUACompliance, etiketli PDF ve sekme sırası gereksinimlerini etkinleştiren bir boole değeridir
Pdf := THotPDF.Create(nil);
try
Pdf.PDFACompliance := '2B'; // PDF/A-2 Level B, drives /CIDSet emission
Pdf.PDFUACompliance := True; // stamps /Tabs /S on annotated pages
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansSC-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Noto Sans SC', [], 12);
Pdf.CurrentPage.TextOut(72, 760, 0, '合规报告');
Pdf.EndDoc;
Pdf.SaveToFile('Report_PDFA.pdf');
finally
Pdf.Free;
end;
Mühendislik dersi
Bundan iki kural çıkar. Birincisi, nesneleri değiştiren her sonlandırma adımı, o nesneler serileştirilmeden önce çalışmak zorundadır ve bir belge motorunun kapanış aşaması, serileştirmenin son eylem olduğu sıralı bir hat olarak okunmalıdır; birkaç eylemden biri değil. İkincisi unutması en kolay olanıdır: bir yayma adımı için hatasızlık, başarının kanıtı değildir. Doğru alt kümeyi oluşturup yanlış, çoktan yazılmış grafiğe uygulayan bir yordam, kendi perspektifinden yanlış bir şey olmadığı için yanlış bir şey raporlamaz. Doğrulamanın geri dönüş koduna değil esere bakması gerekir. Çıktı boyutunu kontrol edin, gömülü fontun bayt uzunluğunu ve /BaseFont önekini okuyun; eksik bir /CIDSet'in sessiz bir eksiği adlandırılmış bir başarısızlığa çevirdiği PDF/A çıktısını veraPDF'e yargılatın
Yazı tipi işlemenin üretici tarafı, yazı tiplerinin rapor çıktısı için nasıl kaydedildiği ve gömüldüğü, rapor çıktısında yazı tipleri ve resimler hakkındaki makalemizde ele alınmıştır. Bu sonlandırma adımlarının standartlara göre kontrol edildiği doğrulama tarafı, PDF/A ve PDF/UA doğrulaması hakkındaki kılavuzda ele alınmıştır. Her ikisi de, bu blogun başka yerlerinde ele alınan yükleme, düzenleme, şifreleme ve imzalama API'lerinin yanı sıra Delphi ve C++Builder için HotPDF Bileşeni'nin bir parçası olarak sunulan burada açıklanan alt kümeleme ve uyumluluk çalışmasıyla eşleşir