💡 Tam çalışan örnek GitHub’da mevcuttur:
nodejs-docker-signing-with-fonts
Giriş
Yazı tipi çözümlemesi, konteyner imzalamasının Node hizmetinizin belge üretip üretmediğini ya da istisna fırlattığını belirleyen kısmıdır. GroupDocs.Signature eksik bir aileyi (family) ikame etmez: görüntüde bulunmayan bir aile adı verildiğinde çağrı yükselir ve hiçbir şey yazmaz. Yazı tipini temizlemek de bir geçici çözüm değildir, çünkü kütüphane kendi varsayılanını ister ve aynı şekilde başarısız olur.
Aile adını belirlemenin üç yolu vardır ve yalnızca biri bir konteynerde hayatta kalır. Bu makale bu yolları karşılaştırır, ardından bunları şekillendiren provizyon ve bağlama davranışlarını ele alır; çünkü Node.js üzerinden Java, bu kütüphanenin diğer platformlarda sunduğundan daha fazla bu iki unsuru barındırır.
Neden Bu, Node.js’de Daha Önemli
Paket bir köprüdür: node-java aynı süreç içinde bir JVM yükler. Bu yüzden bir Node imzalama imajının bir JDK, köprüyi derlemek için node-gyp araç zinciri ve libjvm.so‘ya işaret eden LD_LIBRARY_PATH‘e ihtiyacı vardır; yazı tipleri buna gelmeden önce ayarlanır. node:18-bookworm ise AWT için 6 DejaVu yazı tipi dosyası sağlar – Latin alfabesi için yeterli, CJK için hiçbir şey yok.
Bu kombinasyon, uygulama hatası gibi görünen hatalara yol açar. Eksik bir JVM yolu, eksik bir yazı tipi ve bir marshaling uyumsuzluğu hepsi Error running instance method olarak ortaya çıkar; çünkü node-java, Java tarafında fırlatılan her şey için bu mesajı rapor eder.
Önkoşullar
Node 18 – köprü NAN üzerine inşa edilir; NAN, Node 20 veya 22’deki V8 ile derlenemez ('AccessorSignature' is not a member of 'v8'). JDK 8‑17: JDK 25’te görüntü katmanı Cannot open an image. The image size can not be 0! hatası verir.
Kurulum
npm install @groupdocs/groupdocs.signature
İmaj içinde bu kurulumun çalışması için build-essential ve python3 bulunmalı, ayrıca openjdk-17-jdk-headless ve yükleyici yolu ayarlanmalıdır:
# Gerekli paketleri kur: build-essential, python3 ve openjdk-17-jdk-headless
RUN apt-get update && apt-get install -y build-essential python3 openjdk-17-jdk-headless
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH="${JAVA_HOME}/bin:${PATH}"
# node-java çalışma zamanında libjvm.so'yu dlopen ile açar; varsayılan yükleyici yolunda bulunmaz.
ENV LD_LIBRARY_PATH="${JAVA_HOME}/lib/server:${LD_LIBRARY_PATH}"
Yöntem 1 - Aile adını sabit kodlamak
Herkesin ilk yazdığı sürüm: Arial seç, gönder, devam et. Geliştirici makinesinde çalışır, ilk konteyner çalıştırmada başarısız olur; çünkü Debian imajları Arial yerine Liberation Sans kurar ve bu farklı bir aile adıyla metrik‑uyumludur.
Burada gösterilecek bir kod yoktur; işte bu nokta. Yöntemin tüm içeriği, bir ortamda doğru olan bir dize sabitidir.
Yöntem 2 - Dosya sisteminden yazı tiplerini tespit etmek
Doğal çözüm: yazı tipi dizinlerini tarayın, neler olduğunu görün, bir şey seçin. Yarım kısmı gerçekten işe yarar – envanter, imajın 0 mı yoksa 6 mı yazı tipi içerdiğini söyler:
const roots = [
'/usr/share/fonts',
'/usr/local/share/fonts',
path.join(home, '.fonts'),
path.join(home, '.local', 'share', 'fonts'),
'/System/Library/Fonts',
'/Library/Fonts',
];
Diğer yarısı işe yaramaz. Yazı tipi dosyaları nadiren çağıranın geçmesi gereken aile dizesini taşır: Debian’ın fonts-noto-cjk paketi NotoSansCJK-Regular.ttc kurar, ailesi Noto Sans CJK JP‘dir. Dosya adından türetilen aile NotoSansCJK-Regular olur ve hiçbir şeye çözülmez. Dosya adı tespiti hem mevcut yazı tiplerini kaçırır hem de başarısız olacak aileleri güvenle raporlar.
Envanteri tanılayıcı olarak tutun. Seçim için kullanmayın. Sayı, imajın hiç provizyon edilip edilmediğini yanıtlar; bu da farklı ve aynı derecede yararlı bir sorudur.
Yöntem 3 - Kütüphaneye sormak
Aday aileler için bir kez kullanılabilir imza denemesi yapın ve hatasız olanı tutun. Her aday için bir PDF yazımı gerekir ve bu, gerçek imzanın da aynı çağrıyı yapması nedeniyle yanıtı otoriter olan tek yöntemdir.
for (const candidate of candidates) {
if (tryFamily(sourcePath, candidate) === null) {
return candidate;
}
}
return null;
Node’da sondaj bir ek parçaya ihtiyaç duyar. node-java, her Java istisnasını Error running instance method içine çevirir; bu yüzden gerçek mesaj, sarmalanmış yığın izinden çıkarılmalıdır:
const stack = err.stack || '';
const match = stack.match(/com\.groupdocs\.signature\.exception\.[^\n]*/);
return match ? match[0].trim() : (err.message || String(err));
Bu iki satır olmadan, yazı tipleri olmayan bir konteyner ve bozuk bir JVM yolu aynı logları üretir. Regex’i eklemeden önce tamamen farklı nedenlerle aynı hatayı veren iki konteyneri karşılaştırmak zorunda kaldım.
Sondajın maliyeti
Sondajın itirazı, dosya yazdığı içindir ve gerçekten de öyledir: her aday için küçük bir PDF, hemen silinir. Örnekteki Latin listesi dört, CJK listesi sekiz giriş içerir; bu da soğuk bir başlangıçta hizmet hazır olana kadar en fazla on iki tek‑sayfalık belgeyi geçici dizine yazar.
Bu bir başlangıç maliyetidir, istek başına değil, ve hem çözülen aileleri hem de log satırını sağlar. Temiz bir şekilde başlayan ve ardından köprü hatasıyla ilk müşteri belgesinde başarısız olan bir konteynerle karşılaştırıldığında, on iki geçici dosya zor bir takas değildir.
Yöntemleri Karşılaştırma: Hangi Durumda Hangi Yöntemi Kullanmalı
| Yöntem | En Uygun Kullanım | Ana Avantajlar | Sınırlamalar |
|---|---|---|---|
| Sabit kodlanmış aile | tek bir kontrol edilen ortam | basit, başlangıç maliyeti yok | tam olarak bu aileyi içermeyen herhangi bir imajda kırılır |
| Dosya adı tespiti | imajın ne içerdiğini teşhis etmek | hızlı, imzalama çağrısı yok | dosya adları aile adı değildir, bu yüzden onlardan türetilen seçimler başarısız olur |
| Kütüphane sondajı | konteynerleştirilmiş veya taşınabilir her şey | otoriter, hem dizüstü bilgisayarda hem imajda çalışır | her aday için bir PDF yazılır, bu yüzden başlangıçta çözülüp önbelleğe alınır |
Bilmeniz Gereken İki Bağlama Özelliği
Bir aile çözüldükten sonra, imzalama çağrısının kendisi Node‑özel bir şekle sahiptir. Java API’si bir seçenek listesi alır, ancak bir JavaScript dizisi java.util.List‘e marshallemez; bu yüzden bir liste geçmek Could not find method "sign(java.lang.String, [Ljava.lang.Object;)" hatasına yol açar. Çözüm, tek‑seçenek aşırı yüklemesini zincirlemek ve geçici bir dosya üzerinden aşamalandırmaktır:
new signatureLib.Signature(sourcePath)
.sign(firstOutput, buildTextOptions(LATIN_TEXT, latinFamily, 50));
if (stageTwo) {
new signatureLib.Signature(firstOutput)
.sign(outputPath, buildTextOptions(CJK_TEXT, cjkFamily, 120));
}
İkinci tuhaflık ise geri okuma işlemidir. TextVerifyOptions bu bağlamda çift yönlü çalışmaz: verify aynı genel köprü hatasını yükseltir, bu yüzden örnek bir sentinel döndürür ve unavailable yazar; imzanın başarısız olduğunu taklit etmez. npm paketi 24.12.0 sürümündedir, Aralık 2024’te yayımlanmıştır ve .NET 26.6, Java 26.5 iken 23.6.1 motorunu paketler. İmzalama etkilenmez; yalnızca doğrulama yolu eksiktir.
Üretimde hâlâ Node.js bağlamasını kullanmalı mıyım?
Sadece Latin alfabesiyle imzalama için evet: doğru imzalar, eksik bir yazı tipi sessizce bozulmak yerine hata verir, bu yüzden başarısızlık modu yüksek seslidir. Karma script çalışmaları için, eksik geri okuma özelliğini göz önünde bulundurun; süreç CJK gliflerinin gömülü olduğunu doğrulayamadan sadece kutucuk olarak render edebilir. Aynı pipeline’da .NET veya Java ile küçük bir doğrulayıcı bu boşluğu kapatır.
En İyi Uygulamalar ve İpuçları
- Provizyon sırasını izleyin: JDK ve araç zinciri, yükleyici yolu, yazı tipleri, ardından uygulama. Her katman farklı şekilde başarısız olur ve karıştırmak tanılamayı yavaşlatır.
- Aileleri başlangıçta bir kez çözün ve yazı tipi sayısının yanına loglayın.
- Node 18 ve JDK 8‑17 arasında kilitleyin; her ikisini de rutin yükseltmeler yerine sabit altyapı olarak değerlendirin.
- Yazı tipleri olmayan Dockerfile’ı depoda tutun, böylece hata bir derleme uzakta kalır.
Sonuç
Yazı tipi seçmenin üç yolu, dağıtımda hayatta kalabilen tek yol vardır. Kütüphaneyi sondajlayın, cevabı önbelleğe alın ve envanteri karar vermek yerine tanılayıcı olarak kullanın. Ardından bağlamayı olduğu gibi kullanın: bir seferde bir seçenek imzalayın, Java istisnasını yığın izinden çıkarın ve eksik doğrulamayı gizlemek yerine dürüstçe raporlayın. Örnek depo her iki imajı da oluşturur; burada verilen tüm iddialar iki komutla kontrol edilebilir.