İçeriğe geç
getnextpdf.com

Üretimde yazı tiplerini sağlayın

PDF’iniz dizüstü bilgisayarınızda doğru işlenir, sonra bir kapsayıcıya gönderilir ve bir dizi boş kutu — “tofu” glifi — olarak ya da eksik aksanlar ve Latin olmayan karakterlerle çıkar. Neden neredeyse her zaman aynıdır: seçtiğiniz yazı tipi, dağıtılan görüntüde mevcut değildir.

Yerel, süreç içi NextPDF motoru, yazı tiplerini yazı tipi kayıt defterinin okuyabildiği yazı tipi dosyalarından çözümler. OS veya fontconfig yazı tiplerini otomatik olarak keşfetmez — OS’a kurulu yazı tipi dosyaları yalnızca, bu dosyaları açıkça kaydederseniz veya onları içeren dizini FontRegistry arama yoluna eklerseniz işe yarar. Yalın bir temel görüntüden derlenmiş bir kapsayıcıda apt/apk ile kurulu yazı tipleri yoktur ve olsalar bile, kayıt defterini dosyalarına yönlendirmediğiniz sürece yerel motor bunları yok sayar. Çözüm, gerçek yazı tipi dosyalarını uygulamanızın veya görüntünüzün içinde paketlemek ve onları motora kaydetmektir. Kayıt defteri, TrueType (.ttf), OpenType (.otf) ve TrueType Collection (.ttc) dosyalarını okur; eski Type1 (.pfb) de kabul edilir ancak yeni işler için nadiren gereklidir.

Başlamadan önce, şu parçaların yerinde olduğunu onaylayın:

  • NextPDF core kuruludur.
  • Kullanmayı düşündüğünüz gerçek yazı tipi dosyalarına sahipsiniz ve bunları gömme lisansınız var. Gömme hakları sizin sorumluluğunuzdadır — bkz. Bir TrueType yazı tipini gömün ve alt küme oluşturun.
  • Derlemeniz bu dosyaları dağıtılan yapıta kopyalayabilir.

Bu bir işletim nasıl-yapılır kılavuzudur. Kod en azdır; iş, derlemede ve dosya sistemi düzenindedir. Tek bir yüzeyi kaydetme ve alt küme oluşturmanın API düzeyindeki mekaniği için yukarıda bağlantısı verilen embed-and-subset tarifini okuyun. Bu sayfa, dosyaları makineye almayı ve motoru onlara yönlendirmeyi kapsar.

Yerel motor OS yazı tiplerini neden otomatik olarak bulmaz

“Yerel motor OS yazı tiplerini neden otomatik olarak bulmaz” başlıklı bölüm

İki ayrı işleme yolu vardır ve yazı tipi öyküsü aralarında farklıdır.

  • Yerel süreç içi motor (varsayılan, Document / writeHtml): motor, keşif için işletim sisteminin yazı tipi sistemini veya fontconfig’i çağırmaz. Bir yüzeyi, kaydettiğiniz belirli bir yazı tipi dosyasını okuyan ya da arama yolu olarak yapılandırdığınız bir dizinin içinde birini bulan yazı tipi kayıt defteri aracılığıyla çözümler. apt-get install fonts-noto ile bir yazı tipi kurmak veya fc-cache çalıştırmak tek başına hiçbir şey yapmaz — yerel motor, bu dosyaları yalnızca onları kaydederseniz ya da dizinlerini kayıt defterinin arama yoluna eklerseniz görür.
  • Chrome köprüsü (başsız bir tarayıcıyı süren HTML’den PDF’e işleyici): bu yol tarayıcının normal yazı tipi keşfi aracılığıyla ana makinenin kurulu yazı tiplerini kullanır, dolayısıyla apt/apk yazı tipi paketleri ve fontconfig orada önemlidir.

Genel “bu sistem yazı tipi paketlerini Dockerfile’ınıza kurun” yönergesini okursanız, bu Chrome köprüsü için geçerlidir, bu sayfada ele alınan yerel motor için değil. Yerel üretim için dosyaları paketleyin ve onları kaydedin.

1. Adım — Gerçek yazı tipi dosyalarını paketleyin

“1. Adım — Gerçek yazı tipi dosyalarını paketleyin” başlıklı bölüm

Yazı tipi dosyalarını, sürümlenip her derlemeyle birlikte gönderilmeleri için uygulama ağacınızın içine koyun. Geleneksel bir konum, bir resources/fonts/ dizinidir.

your-app/
├── resources/
│ └── fonts/
│ ├── DejaVuSans.ttf
│ ├── DejaVuSans-B.ttf
│ └── NotoSansCJK-Regular.ttc
└── src/

Dosyaları, motorun dizin aramasının onları aileye ve stile göre bulabilmesi için adlandırın. Belirli bir dosya yerine bir dizin kaydettiğinizde ve daha sonra setFont('DejaVuSans', 'B', 12) çağırdığınızda, motor yapılandırılmış her dizinde DejaVuSans-B.ttf, DejaVuSansB.ttf veya DejaVuSans.ttf gibi dosyalar arar. Dizin araması bu aday adları, yazılı bir sözcükten değil, setFont’a geçirdiğiniz aynı tek harfli stil kodundan oluşturur (B kalın için, I italik için, BI kalın-italik için) — bu nedenle güvenilir biçim Family-<StyleCode>.ttf’dir (örneğin DejaVuSans-B.ttf veya DejaVuSans-BI.ttf), Family-Bold.ttf değil. DejaVuSans-Bold.ttf adlı bir dosya, dizin araması tarafından asla bulunmaz; böyle bir dosyayı kullanmak için onu register() ile açıkça kaydedin — bu yazı tipini ayrıştırır ve dosyanın kendi ad tablolarından okunan aileye ve stile göre dizinler, böylece yazılı dosya adı artık önemli olmaz (bkz. 2. Adım).

Dosyaları görünür kılmanın iki eşdeğer yolu vardır. İkisi de NextPDF\Contracts\FontRegistryInterface arayüzünü uygulayan NextPDF\Typography\FontRegistry üzerinden gider.

Tam yüzeyi denetlediğinizde, bir takma ad altında belirli bir dosyayı kaydedin:

use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();
$registry->register(__DIR__ . '/../resources/fonts/DejaVuSans.ttf', alias: 'DejaVuSans');

register(string $fontFile, string $alias = '', int $fontIndex = 0), .ttf, .otf ve .ttc dosyalarını, ayrıca eski Type1 .pfb dosyalarını (eşlik eden .afm ölçümlerini aynı yoldan yükler) kabul eder; $fontIndex, bir TrueType Collection (.ttc) içindeki bir alt yazı tipini seçer. register(), dosyayı ayrıştırır ve yüzeyi kendi ad tablolarından okunan aileye ve stile göre dizinler, böylece kaydedildikten sonra fiziksel dosya adı önemsizdir. İsteğe bağlı $alias, yüzey için yalnızca ek bir arama adıdır — bir stil kodu değildir ve dosyanın hangi stili sağladığını değiştirmez; setFont()’u yazı tipinin gömülü aile adından farklı bir adla çağırmak istediğinizde geçirin. Ayrıştırılan FontInfo’yu döndürür.

Motorun, denetlediğiniz bir klasörden yüzeyleri ada göre çözümlemesini istediğinizde bir dizin kaydedin:

$registry = new FontRegistry('/var/www/app/resources/fonts');
// or, equivalently, after construction:
$registry->addFontDirectory('/var/www/app/resources/fonts');

FontRegistry yapıcısı bu dizini ilk argümanı olarak alır ve addFontDirectory() daha fazla arama yolu ekler. Yalın bir Document da bağımsız durum için addFontDirectory() sunar.

Kendiniz doldurduğunuz bir kayıt defterini kullanmak için belgeleri, oluşturduğu her belgeye tam olarak o kayıt defterini bağlayan DocumentFactory üzerinden oluşturun:

use NextPDF\Core\DocumentFactory;
use NextPDF\Graphics\ImageRegistry;
$factory = new DocumentFactory($registry, new ImageRegistry(maxCacheBytes: 0));
$doc = $factory->create();
$doc->addPage();
$doc->setFont('DejaVuSans', '', 12);
$doc->cell(0, 10, 'Réndéred wîth a bundled face — no tofu.', newLine: true);
$doc->save('/tmp/out.pdf');

Document::createStandalone() kendi iç kayıt defterini oluşturur, dolayısıyla ayrı bir FontRegistry üzerinde kaydettiğiniz bir yüzey ona görünmez. Üretimde, doldurulan kayıt defterinin kullanımda olanı olması için DocumentFactory (veya çerçevenizin fabrikası) üzerinden gidin.

Her çerçeve tümleştirmesi aynı iki kavramı yapılandırma olarak sunar, dolayısıyla kayıt defterine nadiren doğrudan dokunursunuz. Laravel paketinin nextpdf.php dosyasında, fonts_path (varsayılan NEXTPDF_FONTS_PATH, resource_path('fonts') değerine geri düşerek) arama dizinidir ve preload_fonts, çalışan başlatmasında ayrıştırılan mutlak yazı tipi dosyası yollarının bir listesidir. fonts_path değerini paketlediğiniz dizine yönlendirin; kaydedilen yüzeyleriniz otomatik olarak çözümlenir.

3. Adım — Bir Docker görüntüsünde yazı tiplerini sağlayın

“3. Adım — Bir Docker görüntüsünde yazı tiplerini sağlayın” başlıklı bölüm

Bir kapsayıcıda yazı tipi dosyaları, derleme zamanında kopyalanarak görüntü katmanının parçası olmalıdır. Uygulama kodu ve yazı tipleri, onları resources/fonts/ altında paketlediğinizde birlikte gönderildiği için, normal bir COPY . . zaten onları taşır. Yazı tiplerini derleme bağlamı dışında tutarsanız, onları açıkça kopyalayın ve kaydettiğiniz yolun görüntü içindeki yolla eşleştiğinden emin olun.

# Native engine: NO system font packages are required.
# The native engine does not discover OS-installed fonts automatically; install OS
# font packages (`apt-get install fonts-*`) only if you also register them or point
# the font registry's search directory at their files.
FROM php:8.4-cli
WORKDIR /var/www/app
# Bundle the application, including resources/fonts/, into the image.
COPY . /var/www/app
# Make the bundled directory the engine's font search path.
ENV NEXTPDF_FONTS_PATH=/var/www/app/resources/fonts
CMD ["php", "bin/generate.php"]

Değişmez veya salt okunur bir dosya sisteminde (bir readOnlyRootFilesystem kapsayıcısı, bir sunucusuz görüntü veya sertleştirilmiş bir ana makine), yazı tipi dosyaları üretim zamanında okunur ve asla yazılmaz, dolayısıyla salt okunur bir bağlama sorunsuzdur. Motorun isteyebileceği tek yazma, ayrıştırılmış yazı tipi önbelleğidir: ya o dizine küçük bir yazılabilir birim verin ya da çalışma zamanında hiçbir yazma veya kayıt denenmemesi için başlatmada kayıt defterini ısıtıp kilitleyin (sonraki bölüm).

Uzun ömürlü bir çalışanda, her yüzeyi başlatmada bir kez ayrıştırın, ardından kayıt defterini kilitleyin; böylece istek başına kayıt gerçekleşmez ve bir yanlış yapılandırma sessizce geri düşmek yerine yüksek sesle başarısız olur:

$registry = new FontRegistry('/var/www/app/resources/fonts');
$registry->warmup([
'/var/www/app/resources/fonts/DejaVuSans.ttf',
'/var/www/app/resources/fonts/DejaVuSans-B.ttf',
]);
$registry->lock();

lock()’tan sonra register(), addFontDirectory() ve warmup() fırlatır; bu da “görüntüdeki yanlış yol” hatasını üretimdeki bir tofu sayfası yerine sert bir başlatma başarısızlığına çevirir.

Gereken her yüzeyle bir sayfa işleyen bir dağıtım duman denetimi ekleyin. Aşağıdaki başlık denetimi yalnızca belgenin çıktı ürettiğini doğrular — yazı tipinin ayrıştırıldığını, gömüldüğünü ya da çözümlendiğini bile kanıtlamaz. Motorun bulamadığı bir yüzey, geçerli ve boş olmayan bir PDF yaymaya devam ederken standart bir temel yazı tipine geri düşebilir (ve geçerli katı olmayan davranış altında, bir uyumluluk profili bunun yerine paketlenmiş bir ikame sağlayabilir) — bu nedenle o geri düşme gerçekleştiğinde bile yalnızca bu denetim sessiz bozulmayı yakalamaz. Geri düşmenin her yolda garantili veya sessiz olduğuna güvenmeyin; gömülü programı, aşağıda gösterildiği gibi doğrudan doğrulayın:

$doc = $factory->create();
$doc->addPage();
$doc->setFont('DejaVuSans', '', 12);
$doc->cell(0, 10, 'warmup check', newLine: true);
$pdf = $doc->getPdfData();
// `getPdfData()` would normally throw on a real failure; this header check only
// confirms serialization returned PDF bytes, not that any specific font resolved.
if (!str_starts_with($pdf, '%PDF')) {
throw new RuntimeException('Font warmup smoke check produced no PDF output.');
}

Bir yüzey eksik olduğunda dağıtımı gerçekten başarısız kılmak için yayılan PDF’i gömülü yazı tipi programı açısından denetleyin. Çözümlenen kaydedilmiş bir yüzey, gömülü bir programa sahip kendi yazı tipi sözlüğünü taşır, dolayısıyla varlığını ileri sürmek, başlık denetiminin kaçırdığı, istenen yüzeyin hiç çözümlenmediği (motorun her ne şeye geri düştüğünden bağımsız) durumu yakalar. Programı hangi anahtarın tuttuğu, ana hat biçimine bağlıdır: TrueType ana hatları (.ttf, .ttc) /FontFile2 kullanır, CFF/OpenType ana hatları (PostScript ana hatlı .otf) /FontFile3 kullanır ve eski Type1 (.pfb) /FontFile kullanır.

Tek ihtiyacınız biçimden bağımsız bir “bir yazı tipi programı gömülü” sinyaliyse, yalnızca /FontFile için test edin — çünkü /FontFile hem /FontFile2 hem de /FontFile3’ün bir alt dizesidir, dolayısıyla yalın bir alt dize denetimi zaten her ana hat türüyle eşleşir ve /FontFile2//FontFile3’ü ek || dalları olarak eklemek gereksizdir:

if (!str_contains($pdf, '/FontFile')) {
throw new RuntimeException('No embedded font program found — face fell back.');
}

Ancak yalın bir /FontFile alt dizesi, ana hat türlerini ayırt edemez. Onları ayırt etmek için, /FontFile’ın /FontFile2 veya /FontFile3 üzerinde de tetiklenmemesi için bir sözcük sınırı ile tam belirteç üzerinde eşleştirin:

$isTrueType = preg_match('~/FontFile2\b~', $pdf) === 1; // TrueType (.ttf/.ttc)
$isCffOtf = preg_match('~/FontFile3\b~', $pdf) === 1; // CFF/OpenType (.otf)
$isType1 = preg_match('~/FontFile(?![23])\b~', $pdf) === 1; // Type1 (.pfb)
if (!$isTrueType && !$isCffOtf && !$isType1) {
throw new RuntimeException('No embedded font program found — face fell back.');
}

Her iki durumda da bunu güvenilir bir dağıtım kapısı olarak değil, yalnızca kaba bir buluşsal yöntem olarak ele alın. Serileştirilen PDF üzerinde ham bir bayt araması birkaç nedenle hatalıdır: yazı tipi programları sıkıştırılmış nesne akışlarının içinde bulunabilir (burada /FontFile* asla düz bayt olarak görünmez), artımlı güncellemeler nesneleri ekleyebilir veya üzerine yazabilir, gömülü olmayan veya standart-14 yazı tipleri haklı olarak hiçbir yazı tipi programı taşımaz ve serileştirme farklılıkları (nesne sıralaması, boşluk, ad kodlaması) belirteci taşıyabilir veya gizleyebilir. En iyi durumda bir yüzeyin bir program gömdüğünü onaylar — istediğiniz belirli yüzeyin çözümlendiğini asla.

Gerçek bir dağıtım kapısı için bayt aramasına güvenmeyin. Yayılan PDF’i uygun bir PDF ayrıştırıcısı veya nesne denetçisiyle ayrıştırın ve hedef yüzeyiniz için yazı tipi nesnesinin gömülü bir /FontFile//FontFile2//FontFile3 programı taşıdığını ileri sürün ya da tümleştirmenize uygunsa ürünün sağladığı bir yazı tipi çözümleme ifadesini kullanın. Yukarıdaki belirteç farkındalıklı düzenli ifadeler hızlı bir yerel akıl sağlığı denetimi için yararlıdır, ancak dağıtımı başarısız kılması gereken şey yapısal bir incelemedir. Gömme ve yazı tipi sözlüğü yapısı Bir TrueType yazı tipini gömün ve alt küme oluşturun sayfasında tarif edilmiştir.

  • createStandalone()’un kendi kayıt defteri vardır. Ayrı bir FontRegistry üzerinde kaydedilen bir yüzey, bağımsız bir belgeye görünmez. Kayıt defterinizin etkin olanı olması için DocumentFactory (veya çerçeve fabrikası) kullanın.
  • Stil dosyaları, dosya olarak var olmalıdır. Motor, normal bir yüzeyden kalın veya italik sentezlemez. setFont('DejaVuSans', 'B') çağırırsanız, dizin araması DejaVuSans-B.ttf, DejaVuSansB.ttf veya DejaVuSans.ttf (küçük harf ve .otf varyantları da) arar — adayı sözcük anlamıyla B stil kodundan oluşturur, dolayısıyla asla DejaVuSans-Bold.ttf aramaz. DejaVuSans-Bold.ttf gibi yazılı bir ada sahip bir dosya yalnızca onu register() ile açıkça kaydettiğinizde çözümlenir; bu da onu dosya adından bağımsız olarak dosyanın kendi ad tablolarından okunan aileye ve stile göre dizinler; onu bulması için dizin aramasına güvenmek bir ıskaya yol açar, ardından motor bir temel yazı tipine geri düşebilir (garantili veya her zaman sessiz olmayan bir yol) — bu sayfanın uyardığı bozulma.
  • Akış sarmalayıcı ve uzak yollar reddedilir. Kayıt defteri, bir URI şeması ya da bir null bayt içeren yolları reddeder. Yalnızca yerel dosyaları kaydedin; çalışma zamanında getirilen yazı tipleri için ham baytlarla registerFromBinary() kullanın.
  • Kilitli kayıt defteri değişmezdir. lock() çağırdığınızda, sonraki herhangi bir register(), addFontDirectory() veya warmup() fırlatır. Arama yöntemleri kullanılabilir kalır. Kilitlemeden önce her şeyi kaydedin ve ısıtın.
  • CJK koleksiyonları büyüktür. Bir .ttc’nin doğru alt yazı tipini $fontIndex ile kaydedin ve daha büyük bir gömülü alt küme için bütçe ayırın. embed-and-subset tarifindeki CJK notlarına bakın.
  • Bir yazı tipi dosyası, güvenilmeyen ikili girdidir. Yalnızca güvendiğiniz kaynaklardan yazı tiplerini paketleyin ve son kullanıcılardan kabul edilen herhangi bir yüzeyin kökenini doğrulayın.
  • Isıtmadan sonra kayıt defterini kilitlemek, bir çalışma zamanı mutasyon yüzeyini kaldırır ve bir yol hatasının çıktıyı sessizce bozmak yerine başlatmada başarısız olmasını sağlar.
  • Kullanıcı girdisini kaydedilmiş bir dosya yoluna eklemeyin. Paketlenmiş sabit bir yüzey kümesini kaydedin; bir isteğin keyfi bir dosya sistemi yolu seçmesine izin vermeyin.

Bu kılavuz normatif bir standart iddiasında bulunmaz. Gösterilen her sembol doğrulanmış genel yüzeydir: NextPDF\Typography\FontRegistry (register(), addFontDirectory(), warmup(), lock(), dizin yapıcı argümanı), onun NextPDF\Contracts\FontRegistryInterface sözleşmesi, NextPDF\Core\DocumentFactory::create() ve NextPDF\Core\Document::setFont() / addFontDirectory(). Laravel fonts_path ve preload_fonts anahtarları, nextpdf/laravel paketinin belgelenmiş yapılandırmasıdır. Gömme ve alt küme etiketi davranışı, ISO 32000-2 alıntılarıyla birlikte, Ayrıca bakınız altında bağlantısı verilen embed-and-subset tarifinde belgelenmiştir.