Teknik Makale

HotPDF ile Yazı Tipi Alt Kümelerini Diskte Önbelleklemek

Aynı şablonla, aynı TrueType yüzüyle üretilen ve yalnızca müşterinin soyadında ayrışan iki fatura, bir yazı tipi alt küme önbelleğinde iki ayrı dosya bırakır. Soyad, ilk faturanın hiç kullanmadığı büyük harfli bir Q ve küçük harfli bir z ekler; tutulan glif kümesi değişir ve onunla birlikte alt küme ile önbellek anahtarı da değişir. Kalıcı bir alt küme önbelleği, belgelerinizin glif dağarcığı tekrarladığında kâra geçer ve sunduğu sayaçlar, sizinkinin tekrarlayıp tekrarlamadığını tek bir toplu iş içinde söyler

HotPDF'deki dört yazı tipi-alt kümesi önbellek anahtarının şeması: yazı tipi içeriği, glif kümesi, alt küme modu ve şema sürümü; tek bir adda farklılaşan iki faturanın farklı girişlere düşmesiyle
tek bir müşteri adıyla ayrılan iki fatura iki önbellek girdisidir — önbellek yalnızca belgeler glif dağarcığını paylaştığında kâra geçer

HotPDF'te disk önbelleğini açmak

HotPDF, ona bir klasör verene kadar alt küme önbelleğini kapalı tutar. THotPDF üzerindeki iki özellik bunu denetler: FontSubsetCacheFolder dizini adlandırır, FontSubsetCacheMaxBytes ne kadar tutabileceğini sınırlar; öntanımlı değer 268435456 bayttır (256 MiB). Önbellek yalnızca klasör boş değilken ve bütçe sıfırın üzerindeyken etkindir. Klasörü atamak, eksikse dizini oluşturur ve orada bulunan her şeyi hemen bütçeye indirir

Alt kümeleme gerçek iş olduğundan önbelleklemeye değer. Bir TrueType alt kümesi inşa etmek; bileşik glif kapanışını yürümek, loca ve glyf'i yeniden yazmak, glifleri indeksleyen tabloları yeniden inşa etmek ve PDF'in adresleyebileceği bir CID eşlemesi üretmek demektir. Tek bir belge için bu maliyet gürültüye karışır. Aynı üç yüzle bir döngüde binlerce ekstre üreten bir rapor sunucusunda aynı alt kümeler defalarca inşa edilir ve önbelleğin geri verdiği CPU tam olarak budur

var
  Pdf: THotPDF;
  Info: THPDFFontSubsetCacheInfo;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.EnableFontSubsetting := True;
    // Yol yalnızca ASCII: özellik bir AnsiString'tir
    Pdf.FontSubsetCacheFolder := 'C:\ProgramData\Reports\fontcache';
    Pdf.FontSubsetCacheMaxBytes := 64 * 1024 * 1024;   // varsayılan 256 MiB yerine 64 MiB
    // ... toplu işteki her belge için BeginDoc / çizim / EndDoc ...
    Info := Pdf.GetFontSubsetCacheInfo;
    // LogFmt sizin kendi günlük kayıt yordamınızdır
    LogFmt('subset cache: %d hits, %d misses, %d bytes in %d files',
      [Info.HitCount, Info.MissCount, Info.CurrentBytes, Info.FileCount]);
  finally
    Pdf.Free;
  end;
end;

Önbellek her iki alt kümeleme yolunun da arkasındadır: TrueType ve OpenType-CFF yazı tiplerini kapsayan EnableFontSubsetting üzerinden alt kümelenen yüzler ve RegisterUnicodeTTF ile kaydedilen yüz. Çizim kodunuzda başka hiçbir şey değişmez. Klasör yolu AnsiString olarak bildirilir; sistem kod sayfası dışında karakter içeren bir dizin adı sorun davet eder, ProgramData altında ya da bir hizmet veri dizininde düz ASCII bir yol soruyu tamamen ortadan kaldırır

Bir önbellek anahtarına ne girer

Depolanmış bir alt kümenin yeniden kullanılıp kullanılamayacağına dört giriş karar verir: kaynak yazı tipinin baytlarının tam hali, belgenin tuttuğu glif kümesi, alt küme modu ve önbellek biçim sürümü. Her anahtar, yazı tipi baytlarının SHA-256 özeti ile kullanılan glif kimlikleri bitmap'inin SHA-256 özetinden üretilir; seyrek (sparse) alt küme için öneki v1-, son eki -s, sıkı (compact) alt küme için son eki -c olur. Girdiler diske <key>.hpdfsubset olarak yazılır; böylece bir dizin listelemesi, toplu işinizin hangi modu ürettiğini bir bakışta gösterir

Yazı tipi içeriğini dosya adı yerine özetlemek pratik bir sonuç doğurur. Arial'ı yükseltmek ya da kurumsal bir yüzün daha yeni bir derlemesini takmak anahtarları otomatik olarak yeniler; eski girdiler, çıkarma onları kaldırana dek sadece isabet görmemeye başlar. Doğru çıktı için bir yazı tipi güncellemesinden sonra önbelleği temizlemek zorunda değilsiniz; temizlemek yalnızca alanı daha erken geri kazanır

Alt küme modu, insanların unuttuğu giriştir. CompactFontSubsetting, tutulan glifleri sıkı bir aralığa yeniden numaralandırır ve açık bir /CIDToGIDMap yazar (ISO 32000-1 §9.7.4.2); PDFACompliance ayarlıyken kayıtlı yüz daima sıkı bir alt küme alır. CompressDocument da belgenin süresi boyunca sıkı alt kümeleri açar. Böylece aynı ektreyi bir kez düz PDF olarak, bir kez PDF/A olarak yazan bir hizmet, özdeş glifler için iki girdi kümesi doldurur: biri -s ile biri -c ile biter. OpenType-CFF yüzleri daima seyrek yolu izler

İsabet oranına karar verilen yer glif kümesidir. Sabit bir şablondan gelen ekstreler, değişken verisi çoğunlukla rakam olan formlar, tek bir ürün veri tabanından çizilen kataloglar: bunlar belgeden belgeye aynı glifleri yeniden kullanır. Her belgenin on binlerce glifli bir CJK yüzünün farklı bir dilimini çektiği bir iş yükü çoğunlukla ıskalar, çünkü neredeyse her belgenin bitmap'i yenidir. Ne tür bir iş yükünüz olduğuna karar vermeden önce temsili bir toplu iş çalıştırın ve sayaçları okuyun

Önbelleğin işe yaradığını nasıl bilirsiniz?

GetFontSubsetCacheInfo, dokuz alanlı bir THPDFFontSubsetCacheInfo kaydı döndürür ve HitCount ile MissCount arasındaki oran soruyu doğrudan yanıtlar. WriteCount ve EvictionCount, girdilerin yeniden kullanılmaya yetecek kadar uzun yaşayıp yaşamadığını ya da çok dar bir bütçe tarafından dışarı itilip itilmediğini gösterir. CurrentBytes ve FileCount, klasörün şu anki durumunu tarif eder

HotPDF paneli: isabet oranı, hayatta kalma ve tasarruf olarak gruplanan dokuz GetFontSubsetCacheInfo sayacı; değiştirilecek ayarı adıyla söyleyen üç okumayla
HitCount'un MissCount'a oranı önbelleğin bir şey yapıp yapmadığını yanıtlar; diğer sayaçlar neden yapmadığını söyler

Bunları izlemeye bağlarken iki ayrıntı önem taşır. Yedi olay sayacı THotPDF örneğine aittir ve bileşen oluşturulduğunda sıfırdan başlar; belge başına bir bileşen oluşturan bir hizmet, her okumada bir belgelik etkinlik görür. Buna karşılık CurrentBytes ve FileCount, çağrı anında klasörü numaralandırmaktan gelir; bu, onları o klasörü kullanan her süreci yansıtır ve her çağrıya bir dizin taraması maliyeti yükler. Kaydı toplu iş başına bir kez, sonda okuyun ve belge başına döngüyü ondan uzak tutun

Kalan üç sayaç, uyarıya değer olanlardır. CorruptCount, yükleme sırasında doğrulamada başarısız olup silinen girdileri sayar; her biri aynı zamanda ıskalama olarak da sayılır. Bir elektrik kesintisinden sonra birkaç tanesi normaldir, sürekli bir sızıntı depolamayı işaret eder. RejectedCount, önbelleğin depolamayı reddettiği alt kümeleri sayar; tipik olarak tek bir alt kümenin bütün bütçeden büyük olması yüzünden. WriteFailureCount, tamamlanamayan yazmaları sayar; pratikte bu, neredeyse daima hizmet hesabının yazamadığı bir klasördür. Üçü de belge üretimini kesintiye uğratmaz; dolayısıyla yazma izni sorunu olan bir önbellek, hiç önbellek olmamasıyla birebir aynı PDF'leri üretir. Sadece hiç hızlanmaz

// SubsetCacheNeedsAttention sizin ekleyeceğiniz bir yardımcıdır;
// eşikler örnektir, toplu iş boyutunuza göre ayarlayın
function SubsetCacheNeedsAttention(const Info: THPDFFontSubsetCacheInfo;
  out Reason: string): Boolean;
begin
  Reason := '';
  if Info.WriteFailureCount > 0 then
    Reason := 'cache folder is not writable for this account'
  else if Info.RejectedCount > Info.WriteCount then
    Reason := 'subsets exceed FontSubsetCacheMaxBytes, raise the budget'
  else if (Info.EvictionCount > Info.WriteCount div 2) and (Info.HitCount < Info.MissCount) then
    Reason := 'entries are evicted before they are reused'
  else if Info.CorruptCount > 10 then
    Reason := 'repeated damaged entries, check the storage';
  Result := Reason <> '';
end;

Bütçeler, çıkarma ve klasörü boşaltan anahtar

Çıkarma, dosya değişiklik zamanına göre en az yakın kullanılan (LRU) şeklindedir. Her başarılı yükleme girişin zaman damgasına dokunur, her yazmayı bir kırpma izler ve klasör bütçeye yeniden sığana kadar en eski dosyalar önce gider. FontSubsetCacheMaxBytes'i çalışma zamanında düşürmek, bir sonraki yazmayı beklemek yerine yerinde kırpar; böylece disk alanı alarmına tepki veren bir hizmet, önbelleği karar verdiği an küçültebilir

Bu ani etkinin keskin bir tarafı vardır. Klasör ayarlıyken bütçeyi sıfıra çekmek sıfıra kırpar; bu da içindeki her .hpdfsubset dosyasını siler. Buna karşılık FontSubsetCacheFolder'ı boş bir dizeye ayarlamak önbelleği kapatır ve dosyaları tam olarak bulundukları yerde bırakır. İki anahtar da bir sonraki belge için önbellekleme devre dışı bırakır; ama ikisinden yalnızca biri bir haftalık sıcak girdileri korur. Sorun giderme için klasör özelliğini boşaltın ve bütçeye dokunmayın

// Tanısal bir çalıştırma için önbelleği duraklatın, depolanmış her girişi tutun
Pdf.FontSubsetCacheFolder := '';

// Tehlike: klasör ayarlıyken sıfır bütçe, klasörü hiçliğe kırpar
// Pdf.FontSubsetCacheMaxBytes := 0;

// Bilinçli sıfırlama: *.hpdfsubset ve artık geçici dosyaları siler,
// olay sayaçları değerlerini korur
Pdf.FontSubsetCacheFolder := 'C:\ProgramData\Reports\fontcache';
Pdf.ClearFontSubsetCache;

Önbellek duraklatılmış bir tanısal çalıştırma, onu dışlamanın en temiz yoludur. /BaseFont içindeki altı harfli alt küme etiketi tutulan glif kümesinden türetilir; böylece aynı glifler, önbellekle ya da önbelleksiz aynı etiketi üretir ve gömülü yazı tipi programları eşleşmelidir. Tüm dosya özetleri, oluşturma tarihleri ve belge kimliği yüzünden yine de farklılaşır; bu yüzden dosyaları değil yazı tipi akışlarını karşılaştırın

Hasarlı ya da yarım yazılmış bir girdiye ne olur?

Hasarlı bir girdi silinir ve alt küme, o girdiden gelen hiçbir şey bir PDF akışına ulaşmadan önce normal biçimde inşa edilir. Yükleme sırasında HotPDF dosya imzasını, biçim sürümünü, alt küme modu bayrağını, glif sayısını kaynak yazı tipiyle, bildirilen uzunlukları gerçek dosya boyutuyla ve saklanan yazı tipi programının sfnt başlığını denetler. Herhangi bir uyuşmazlık girdiyi çöpe atar, CorruptCount'ı yükseltir ve bellek içi alt kümelemeye düşer. Bozuk bir yazı tipi programını bir müşterinin PDF'ine sokmak, haftalar sonra başka birinin makinesindeki bir görüntüleyicide, nedeninden çok uzakta yüzeye çıkardı; bu doğrulamanın varlık sebebi tam olarak o başarısızlığı önlemektir

HotPDF'de hasarlı önbellek girişi işlemenin akış şeması: kullanmadan önce doğrula, herhangi bir PDF akışından önce reddet ve kaldır, bellek içi alt kümelemeye düş
bozuk girdiler açık biçimde başarısız olur — belge yine de üretilir ve PDF/A çıktısı önbelleği tutar, çünkü girdiler CID yeniden eşlemesini saklar

Yarım yazılmış girdiler zaten hiç ortaya çıkmaz. Bir yazma, adı işlem kimliğini ve bileşen örneğini taşıyan geçici bir dosyaya yapılır, sonra tek bir değiştir ve temizle yeniden adlandırmasıyla son adın üzerine taşınır. Yazma ortasında bir çökme, ClearFontSubsetCache'in süpürdüğü başıboş bir .tmp- dosyası bırakır; gerçek girdi ya eksiksiz eski sürüm ya eksiksiz yeni sürümdür. Sıkı girdiler ayrıca yazı tipi programının yanında eski-yeni glif eşlemesini de saklar; böylece önbelleğe alınmış bir sıkı alt küme aynı /CIDToGIDMap'i yeniden inşa eder ve PDF/A çıktısı, önbelleği atlamadan kullanabilir

Klasör nerede durmalı?

Üç koşul buna karar verir: hizmet hesabı oraya yazabilmelidir, klasör yerel depolamada durmalıdır ve hiçbir dağıtım adımı onu silmemelidir. Bir ağ paylaşımı her aramayı bir gidiş-dönüşe çevirir ve yeniden adlandırma semantiğini dosya sunucusunun eline verir. Yükleyicinin yeniden oluşturduğu uygulama dizini içindeki bir klasör, önbelleğin her güncellemeden sonra soğuk başladığı anlamına gelir; bu da faydayı, tam olarak performansı en yakından izlediğiniz gün sessizce siler

Birçok bileşen ve birçok süreç tek bir yerel klasörü paylaşabilir. Geçici adlar asla çarpışmaz, son yeniden adlandırma atomiktir ve kaybedilen bir yarışın maliyeti, bir ıskalama ya da bir sonraki belgede yeniden denenmesi gereken bir yazmadan kötüsü olmaz. Paylaşım tek makinede daha iyi varsayılandır, çünkü her örnek önbelleği diğerleri için ısıtır. Görünür tek yan etki: her örneğin sayaçları yalnızca kendi trafiğini tarif ederken CurrentBytes ve FileCount herkesinkini tarif eder ve ara sıra görülen bir WriteFailureCount artışı, aynı girdiyi aynı anda değiştiren iki süreçten gelebilir. Salt okunur bir klasör mevcut girdileri sunmayı sürdürür; sadece zaman damgalarını tazeleyemez ya da yenilerini ekleyemez

Önbellek ne zaman yanlış kaldıraçtır?

Önbellek yinelenen işi ortadan kaldırır. Bir toplu işin ilk belgesi için hiçbir şey yapmaz ve glif kümeleri asla yinelenmeyen iş yüklerine az şey yapar. Çıktı, öngörülemeyen metinlerde kullanılan tek bir büyük CJK yüzüyle domine ediliyorsa, hangi gliflerin her alt kümeye çekildiğine ve nedenine bakın; bu, yazı tipi alt kümesi kapanışı ve glifleri şekillendirme notlarında ele alınır. Amaç CPU değil dosya boyutuysa, CompressDocument ve sıkı yazı tipi alt kümeleri yazısı, sıkı modun gömülü yazı tipinde neyi değiştirdiğini açıklar. Bir toplu iş, yazı tipleriyle ilgisi olmayan nedenlerle yavaş çıkarsa, yazı tipleri ve görsellerle rapor çıktısı gezintisi zamanın gerisinin genellikle nereye gittiğini gösterir; EndDoc yazı tipi alt kümesi sıralaması yazısı ise alt kümelerin belge tamamlanması sırasında ne zaman inşa edildiğini kapsar

Alt küme önbelleği bileşenin içinde yaşadığı için, bir Delphi ya da C++Builder rapor sunucusu onu bir klasör yolu ve bir bütçe ayarlayarak devralır; dağıtılacak ya da izlenecek ek bir hizmet yoktur. Yazı tipi gömme, alt kümeleme ve PDF üretiminin geri kalan özellikleri HotPDF Delphi bileşeni sayfasında listelenir