💡 Tam çalışan örnek GitHub’da mevcuttur:
sign-documents-in-docker-fonts-java

Dokuz Ay Süren Sözleşme İmzalama Servisi

Konteyner font temini, bir Java imzalama servisinin üretimde mi yoksa sadece yazdığınız testlerde mi çalışacağını belirleyen adımdır. Bu önemlidir çünkü hata planlıdır: bir JRE görüntüsü doğru görünmesi için yeterli font kapsamı sağlar, ardından belirli bir belge gelene kadar geri kalanını tutar.

Şekline bir bakalım. Bir belge iş akışı sözleşmeleri imzalar, eclipse-temurin:17-jre üzerinde dağıtılmıştır ve çalışır. Dokuz ay sonra şirket, Japonya’daki ilk müşterisini imzalar, adını imza metnine ekler ve iş Specified font file was not found hatasıyla başarısız olur. Serviste hiçbir şey değişmedi. Görüntü CJK kapsamına sahip değildi; hiçbir belge bunu talep etmemişti.

Teknik neden basittir. eclipse-temurin:17-jre AWT için 8 DejaVu font dosyası paketler; bu dosyalar Latin, Yunan ve Kiril alfabesini kapsar. GroupDocs.Signature eksik bir aileyi ikame etmez, bu yüzden Japonca destekli bir font isteği, bozulmak yerine başarısız olur ve font ayarlanmamış bırakmak da yardımcı olmaz çünkü kütüphane o zaman Times New Roman’ı ister; o da yoktur.

Neden Bu, Fontsuz Bir Görüntüden Daha Kötü?

.NET ve Python temel görüntüleri sıfır font ile gelir. Bu daha iyi bir hata: ilk imza, ilk test çalıştırmasında başarısız olur ve birisi servisi gönderilmeden önce düzeltir.

Bir JVM görüntüsü kısmen başarısız olur; bu da pahalı bir versiyondur. Hata zaten üretimde olan kodda yaşanır, müşteri verileriyle tetiklenir, dağıtımla ilgili bir şeyle değil ve nöbetçi kişi aylarca kimsenin dokunmadığı bir servisten gelen font hatasını görür. Olay maliyeti düzeltme değildir – düzeltme tek bir Dockerfile katmanıdır – saatlerdir ki kimse fontların dahil olduğunu düşünmez.

Bu asimetri, font kapsamını başlatmada doğrulamanız gerektiği argümanını destekler; keşfetmek yerine.

Ayrıca kimlerin ödeyeceğini değiştirir. Fontsuz bir görüntü, bir geliştiricinin kurulum sırasında yirmi dakikasını alır. Kısmen kapsanan bir görüntü, bir nöbetçi mühendise yardımcı olmayan bir zamanda bir saat, geciken sözleşmenin değeri ve kimsenin bir değişikliğe bağlayamadığı bir olay sonrası inceleme maliyeti getirir. İkisi arasındaki teknik fark bir Dockerfile’da dört paket.

Temin Gerçekte Ne Kadar Maliyetli?

Çalışma aşamasında dört Debian paketi:

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/*

Görüntü boyutu her zaman itiraz edilen konudur ve spesifik olmak gerekir: CJK paketi büyük olandır, diğer üçü küçüktür ve belgeleriniz Latin dışı isimler taşıyabiliyorsa hiçbiri isteğe bağlı değildir. Belgelerinizin gerçekten ihtiyaç duyduğu paketleri kurun ve içgüdüyle kırpmak yerine geri okuma ile doğrulayın.

fontconfig çözücü ve hata ayıklama için fc-list dir. fonts-dejavu-core JRE’nin zaten paketlediği şeyi tekrar eder; bu kasıtlıdır: temel görüntü değişirse görüntüyü dürüst tutar. fonts-liberation önemlidir çünkü Windows’ta oluşturulan belgeler Arial ve Times New Roman’ı isimle referans verir ve metrik‑uyumlu render bekler. fonts-noto-cjk yukarıdaki olayın ihtiyaç duyduğu pakettir.

Bir Aileyi Çözmek, Birini İsimlendirmek Yerine

Temin tek başına yeterli değildir; kod hâlâ var olan bir aileyi isimlendirmelidir. Taşınabilir yol, kütüphaneye sormaktır: her aday için bir deneme imzası yap, atma yapmayan ilkini tut.

for (String candidate : candidates) {
    if (tryFamily(sourcePath, candidate) == null) {
        return candidate;
    }
}
return null;

Sondaj, geçici dizine normal bir imza çağrısıdır; başarısızlık bir istisna yerine değer olarak döndürülür:

SignatureFont font = new SignatureFont();
font.setFamilyName(familyName);
font.setSize(10);
options.setFont(font);
signature.sign(scratch.getAbsolutePath(), options);
return null;

Dosya adı tespiti, eşdeğer gibi görünen ama olmayan bir kısayoldur. Debian’ın fonts-noto-cjk paketi NotoSansCJK-Regular.ttc kurar; ailesi Noto Sans CJK JP dir, bu yüzden dosya adı eşleştirmesi hem fontları kaçırır hem de çözülemeyecek aileleri rapor eder.

Dürüstçe Bozulma

Çözüm yerinde olduğunda iki hata sınıfı temiz şekilde ayrılır. Latin ailesi yoksa görüntü hiç imza yapamaz; bu konteynerin durması gerekir. CJK ailesi yoksa bir imza atlanır ve çalışma bir uyarı ile devam eder:

List<SignOptions> options = new ArrayList<>();
options.add(buildTextOptions(LATIN_TEXT, latinFamily, 50));

if (cjkFamily != null) {
    options.add(buildTextOptions(CJK_TEXT, cjkFamily, 120));
}

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

Bu ayrım operasyonel olarak önemlidir. “kullanılabilir font ailesi yok” mesajıyla başlangıçta çıkan bir konteyner dağıtım sorunudur; dağıtan kişi tarafından yakalanır. Teslim edilen bir belgede sessizce eksik bir imza ise uyum sorunudur; alıcı tarafından fark edilir. Ölümcül durumu sıfır olmayan bir çıkış koduna bağlamak, hataları ilk kategoriye yönlendirir.

Ardından sonucu geri okuyun; çünkü CJK kapsamı olmadan yazılmış bir CJK imzası, hiçbir şey yükseltmeden boş kutular olarak görünebilir:

TextSearchOptions options = new TextSearchOptions();
options.setAllPages(true);
List<TextSignature> found = signature.search(TextSignature.class, options);

Zaten Yayınlanmış Bir Takım Bu Durumda Ne Yapmalı?

Font katmanını ekleyin, başlatmada çözüm ekleyin ve her iki sonucu da servisin ilk satırına loglayın; böylece bir sonraki mühendis gerçekten görebilir. Değişiklik bir Dockerfile düzenlemesi ve yaklaşık otuz satır koddur; bir müşteri tetiklemeli olayı, ya bilinen kapsamla başlayan ya da başlamayı reddeden bir konteynere dönüştürür. Mevcut imzalı belgeler etkilenmez; sadece yenileri CJK yolunu kazanır.

Zaten Çalıştırdığınız Bir Görüntüyü Kontrol Etmek

Herhangi bir şeyi değiştirmeden önce mevcut görüntünüzün neye sahip olduğunu bilmek iyidir. Dışarıdan iki komut bu soruya cevap verir:

docker run --rm your-image sh -c "ls -R /usr/share/fonts | head"
docker run --rm your-image sh -c "fc-list : family | sort -u | head -20"

İlk komut font dosyalarını listeler, ikincisi bir çözücünün döndüreceği aile adlarını listeler; aradaki boşluk dosya adı eşleşmesinin neden başarısız olduğunu gösterir. fc-list eksikse, bu kendi başına bir cevaptır: fontconfig kurulu değildir ve herhangi bir aile araması kördür.

Servisin içinde eşdeğer kontrol, başlatma logunda çözülen ailelerin yanına eklenir. fonts on disk: 8, latin: DejaVu Sans, cjk: (none) gibi bir satır, bir sonraki kişiye bu konteynerin neyi imzalayabileceğini ve neyi imzalayamayacağını tam olarak söyler; bu da sabah üçte okuyacakları bir istisna mesajından çok daha faydalıdır.

JVM Detayı Kimse Beklemiyor

Java’ı özellikle etkileyen bir başka şey daha var ve bu fontlarla ilgili değil. GroupDocs Maven artefaktı imzalı bir “fat jar”dır. Bunu gölgeli bir jar’a yeniden paketlemek NoClassDefFoundError: com/groupdocs/signature/options/search/SearchOptions hatasına yol açar ve META-INF/*.SF|RSA|DSA dosyalarını silmek yeterli değildir: MANIFEST.MF 19 MB’lık giriş bazlı özetler taşır ve ana bölümüne kadar kırpılmalıdır. Örnek, bir dependency/ diziniyle düz sınıf yolu üzerinde çalışarak sorunu önler; hiçbir şey gölgelendirilmez.

Bunu belirtiyorum çünkü hem kısmi font kapsamı hem de imzalı jar aynı şekli paylaşır: JVM yolu, kodunuz gibi görünen ama olmayan bir şekilde başarısız olur. İkisi de adlandırıldıktan sonra savunması ucuzdur: çalışan bir sınıf yolu sabitleyin ve temel görüntüye güvenmek yerine başlatmada font kapsamını doğrulayın. Yeniden tasarım gerektirmez; ikisi de bir uygulama hatasından ayırt edilemeyen bir olay sınıfını ortadan kaldırır.

Sonuç

Konteyner içinde bir Java imzalama servisi, öngörülebilir olmak için sadece bir Dockerfile katmanı ve bir başlatma kontrolü uzaktır. fontconfig, DejaVu, Liberation ve Noto CJK kurun; aileyi varsaymak yerine sondajla çözün; gömülemeyenleri atlayın; geri okuyarak doğrulayın. Örnek depo her iki görüntüyü de gönderir; böylece kapsam ve kapsam yokluğu arasındaki fark bir olaydan ziyade iki derleme ile görülür.

Ek Kaynaklar