💡 Tam çalışan örnek GitHub’da mevcuttur:
sign-pdf-in-linux-container-fonts-dotnet

Eski Yöntem Acı Vericiydi

Servis faturaları imzalıyor. Üç yüz font yüklü bir dizüstü bilgisayarda çalışıyor, incelemeden geçiyor ve bir Cuma günü konteynerleştiriliyor. Pazartesi kümedeki ilk iş, Sign document error: Font Arial was not found hatasıyla sıfırdan çıkıyor ve birisi, mcr.microsoft.com/dotnet/runtime:8.0 imajının aslında hangi fontları içerdiğini sormadan önce sabahı yığın izlerini okuyarak geçiriyor.

Cevap: hiçbiri. Bu makaledeki örnek çalıştırılan imajda sıfır font dosyası bulunuyor.

Diğer çalışma zamanlarının nasıl karşılaştırıldığını bilmek faydalı, çünkü hata her birinde farklı görünüyor. eclipse-temurin:17-jre 8 DejaVu dosyası, node:18-bookworm ise 6 dosya paketliyor; ikisi de AWT için, bu yüzden JVM ve Node imajları Latin metni sessizce imzalıyor ve sadece Japonca ya da Çince bir dize geldiğinde çöküyor. python:3.11-slim sıfır gönderiyor, .NET çalışma zamanı imajı gibi, bu yüzden ilk imzada başarısız oluyor. Hiçbiri CJK’yi ücretsiz sağlamıyor.

Konteyner font temini, GroupDocs.Signature for .NET ile Linux imajında metin imzalamanın çalışmasını sağlayan adımdır. Kütüphane eksik bir aileyi otomatik olarak değiştirmediği için önemlidir: yüklü olmayan bir font adı vermek hata oluşturur ve belge yazılmaz. Bu makale, fontsuz imajı düzeltmiş olanla yan yana koyar, neyin değiştiğini gösterir ve aynı kodun geliştirici makinesinde çalışmasını sağlayan çalışma zamanı çözümlemesini ele alır.

Daha İyi Bir Yol Var

İki şey doğru olmalı. İmajda en az bir font olmalı ve kod hangi font olduğunu varsaymayı bırakmalı.

İlk şey bir Dockerfile katmanı. İkinci şey ise bir çözümleme adımı: Arial gibi sabit bir isim kullanmak yerine, kütüphaneye birkaç aday aileden hangisini gerçekten kullanabileceğini sor ve çalışan ilkini tut. Sonuç, ince bir konteynerde, Windows’ta ve CI’da değişiklik yapmadan çalışır, çünkü ortamı kontrol etmediği sürece hiçbir şey iddia etmez.

İşlemeyen bir şey var ve bunu açıkça belirtmek gerekir, çünkü insanların ilk denediği şey budur: fontu ayarlamamak. SignatureFont olmadan, GroupDocs.Signature kendi varsayılanı olan Times New Roman’ı ister; fontsuz imajda bu da yoktur. Çağrı aynı şekilde başarısız olur.

Yeni Yol: İki İmaj, Bir Fark

Adım 1 - İmajda Ne Var Bir Bakın

Herhangi bir şey imzalamadan önce font dosyalarını listeleyin. Sayı, belirsiz bir istisnayı teşhis haline getirir; çünkü sıfır font ve yanlış aile adı farklı düzeltmeler gerektirir:

string[] roots =
{
    "/usr/share/fonts",
    "/usr/local/share/fonts",
    Path.Combine(home, ".fonts"),
    Path.Combine(home, ".local/share/fonts"),
    Environment.GetFolderPath(Environment.SpecialFolder.Fonts),
    "/System/Library/Fonts",
    "/Library/Fonts",
};

Eksik olanı not edin: System.Drawing. System.Drawing.Common .NET 7’den itibaren yalnızca Windows’ta bulunur ve Linux’ta hata fırlatır; bu yüzden üzerine inşa edilen font kodu konteynerde ikinci, alakasız bir nedenden dolayı başarısız olur.

Adım 2 - Font katmanını ekleyin

Dört paket, bir RUN ve hata ortadan kalkar:

RUN apt-get update && apt-get install -y --no-install-recommends \
        fontconfig \
        fonts-dejavu-core \
        fonts-liberation \
        fonts-noto-cjk \
    && fc-cache -f \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

fontconfig çözücüdür ve hata ayıklama için fc-list sağlar. fonts-dejavu-core Latin, Yunan ve Kiril minimumunu sunar. fonts-liberation Arial, Times New Roman ve Courier New için metrik‑uyumlu ikameler sağlar; Windows’ta oluşturulan belgeler aslında bunları referans alır. fonts-noto-cjk ise Çince, Japonca ve Koreceyi kapsar.

Adım 3 - Bir aileyi isimlendirmek yerine çözümleyin

Taşınabilir yol, her aday için geçici bir imza denemek ve hata atmayan ilkini tutmaktır:

foreach (string candidate in candidates)
{
    if (TryFamily(sourcePath, candidate).Ok)
    {
        return candidate;
    }
}

return null;

Dosya adı tespiti cazip bir kısayoldur ama yanlıştır. Debian’ın fonts-noto-cjk paketi NotoSansCJK-Regular.ttc kurar; ailesi Noto Sans CJK JP dir. Dosya adı eşleşmesi, mevcut fontları kaçırır ve SignatureFont‘a geçirildiğinde çözülemeyecek aileleri iddia eder.

Adım 4 - Çözülenleri imzalayın, imzaladıklarınızı doğrulayın

Çözülen bir Latin ailesi zorunludur; çözülen bir CJK ailesi isteğe bağlıdır ve yokluğu atlama, çökme anlamına gelmez:

var options = new List<SignOptions>
{
    BuildTextOptions(LatinText, latinFamily, top: 50),
};

if (cjkFamily is not null)
{
    options.Add(BuildTextOptions(CjkText, cjkFamily, top: 120));
}

SignResult result = signature.Sign(outputPath, options);

Ardından dosyayı geri okuyun, çünkü CJK fontu olmadan CJK boş kutucuklar olarak görünebilir ve hiçbir şey fırlatmaz:

var options = new TextSearchOptions { AllPages = true };
List<TextSignature> found = signature.Search<TextSignature>(options);

Yan Yana: Önce vs. Sonra

Dockerfile.nofonts Dockerfile
İmajdaki font dosyaları 0 DejaVu, Liberation, Noto CJK
Latin metin imzası başarısız, çıkış 3 yazıldı ve geri okundu
CJK metin imzası başarısız yazıldı ve geri okundu
Görünür hata Font <name> was not found yok
Kod farkı yok - aynı ikili yok - aynı ikili

Son satır önemli. İki çalıştırma arasında uygulamada hiçbir şey değişmedi. Örnek deposu her iki dosyayı da gönderdiği için karşılaştırma iki docker build komutuyla yapılır, tahmine dayanmaz. Fontsuz varyantı da depoda tutun: birisi altı ay sonra temel imajı değiştirirse hatayı en hızlı şekilde yeniden üretmenin yolu budur ve imzalar sessizce kaybolur.

Neden Tüm Fontları Yüklemiyoruz?

Çünkü imaj boyutu gerçek bir kısıtlamadır ve yukarıdaki dört paket zaten belgelerin çoğunun kullandığı betikleri kapsar. fonts-dejavu-core tek başına Latin, Yunan ve Kiril imzalama için yeterlidir; Liberation, belgeler Windows ailelerini isimle referans ettiğinde önem kazanır; Noto CJK ise gerçekten büyük bir pakettir ve yalnızca Doğu Asya metni imzalarsanız kendi başına maliyet getirir. Belgelerinizin ihtiyacı olanları yükleyin, ardından geri okuma ile doğrulayın.

Gerçek Dünya Örneği: Toplu İmza İşçisi

Bir kuyruk işçisi geceleri birkaç bin PDF imzalar. Başlangıçta çözümleme yaparak, kullanacağı aileleri bir satırda loglar ve hiçbir aile çözülemezse kuyruğa dokunmadan önce çıkar; böylece mesaj başına başarısızlık yerine tek satırlık bir nedenle konteynerin başlamasını engeller. Bu başlangıç kontrolü, bir font sorununun başarısız iş akışları akışından, tek satırlık bir nedenle başlamayan bir konteynere dönüşmesini sağlar.

Sorgulama maliyeti başlangıçta göz ardı edilecek kadar küçüktür ve belge başına tekrarlanacak kadar büyük değildir. Her sorgulama geçici bir dosyaya gerçek bir imza yazar; bu yüzden Latin listesi en fazla dört, CJK listesi ise en fazla sekiz imza üretir, hepsi tek sayfalık PDF’ye karşıdır. Bir kez çözümleyin, iki aile adını önbelleğe alın ve belge başına yol, öncekine tamamen aynı kalır: seçenekleri oluşturun, Sign çağırın, sonuç sayısını okuyun.

Ben bu sürümde bir öğleden sonrayı kaybettim. Font dizinini taradı, NotoSansCJK-Regular.ttc buldu, CJK’nin mevcut olduğunu bildirdi ve ardından o dosya adından türettiğim her aile adında başarısız oldu. Gerçek bir imza ile sorgulama hem daha basit hem de doğruydu.

Konteynerde Başka Ne Sorun Çıkar?

Bir şey daha, ve bu fontlarla ilgisi yok: InvariantGlobalization=true. Bu, .NET imajından ICU’yu çıkarmak için standart bir tavsiyedir ve GroupDocs.Signature ile birlikte kullanıldığında ilk new Signature(...) çağrısının CultureNotFoundException: ... en-US is an invalid culture identifier hatasını fırlatmasına neden olur; çünkü SignatureSettings bir CultureInfo("en-US") oluşturur. Küreselleştirmeyi etkin tutun ve ICU’nun imajda kalmasına izin verin. Platform desteğini kontrol etmek için system requirements sayfasına bakın, temel imaj seçmeden önce.

Sonuç

Yerel olarak çalışan ve Docker’da başarısız olan bir imzalama servisi neredeyse her zaman font eksikliği nedeniyle olur ve çözüm dört paketlik bir katman ve bir aileyi varsaymak yerine çözümleyen kod eklemektir. Örneği iki imajdan oluşturun, yan yana çalıştırın ve [fonts] satırlarını okuyun: tüm argüman bu tek karşılaştırmada yer alır.

Ek Kaynaklar