💡 Contoh kerja penuh tersedia di GitHub:
sign-pdf-in-linux-container-fonts-dotnet

Cara Lama Sangat Menyakitkan

Layanan menandatangani faktur. Ia berjalan di laptop dengan tiga ratus font terpasang, lolos tinjauan, dan dikontainerkan pada hari Jumat. Pada hari Senin pekerjaan pertama di klaster keluar dengan kode non‑zero Sign document error: Font Arial was not found, dan seseorang menghabiskan pagi membaca jejak tumpukan sebelum ada yang berpikir menanyakan font apa yang sebenarnya ada di dalam image mcr.microsoft.com/dotnet/runtime:8.0.

Jawabannya tidak ada. Nol berkas font, diukur pada image tempat contoh artikel ini dijalankan.

Perlu diketahui bagaimana runtime lain dibandingkan, karena kegagalan terlihat berbeda pada masing‑masing. eclipse-temurin:17-jre menyertakan 8 berkas DejaVu dan node:18-bookworm menyertakan 6, keduanya untuk AWT, sehingga image JVM dan Node menandatangani teks Latin dengan tenang dan hanya gagal ketika string Jepang atau Cina muncul. python:3.11-slim tidak menyertakan apa‑apa, seperti image runtime .NET, sehingga gagal pada tanda tangan pertama. Tidak ada yang mendapatkan CJK secara gratis pada satupun dari mereka.

Penyediaan font dalam kontainer adalah langkah yang membuat penandatanganan teks berfungsi di image Linux dengan GroupDocs.Signature untuk .NET. Ini penting karena perpustakaan tidak menggantikan family yang hilang: menyebutkan font yang tidak terpasang memicu error dan tidak menulis dokumen apa pun. Artikel ini menempatkan image tanpa font berdampingan dengan yang sudah diperbaiki, menunjukkan apa yang berubah, dan membahas resolusi waktu‑jalan yang membuat kode yang sama tetap bekerja di mesin pengembang.

Ada Cara yang Lebih Baik

Dua hal harus benar. Image harus memiliki setidaknya satu font, dan kode harus berhenti mengasumsikan font mana.

Yang pertama adalah lapisan Dockerfile. Yang kedua adalah langkah resolusi: alih‑alih meng‑hard‑code Arial, tanyakan ke perpustakaan family mana dari beberapa kandidat yang sebenarnya dapat digunakan, dan pertahankan yang pertama berhasil. Hasilnya berjalan tidak berubah di kontainer slim, di Windows, dan di CI, karena tidak pernah menegaskan apa pun tentang lingkungan yang belum diperiksa.

Satu hal yang tidak berhasil, dan penting untuk dinyatakan secara jelas karena biasanya hal pertama yang dicoba orang: membiarkan font tidak disetel. Tanpa SignatureFont, GroupDocs.Signature meminta defaultnya sendiri, Times New Roman, yang juga tidak ada di image tanpa font. Panggilan gagal dengan cara yang sama.

Cara Baru: Dua Image, Satu Perbedaan

Langkah 1 - Lihat apa yang ada di image

Sebelum menandatangani apa pun, daftarkan berkas‑berkas font. Jumlahnya mengubah pengecualian yang samar menjadi diagnosis, karena nol font dan nama family yang salah memerlukan perbaikan yang berbeda:

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",
};

Catat apa yang tidak ada: System.Drawing. System.Drawing.Common bersifat hanya Windows mulai .NET 7 dan melempar error di Linux, sehingga kode font yang dibangun di atasnya gagal di kontainer untuk alasan kedua yang tidak terkait.

Langkah 2 - Tambahkan lapisan font

Empat paket, satu RUN, dan kegagalan menghilang:

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 adalah resolver dan memberi Anda fc-list untuk debugging. fonts-dejavu-core adalah minimum untuk Latin, Greek, dan Cyrillic. fonts-liberation menyediakan pengganti metrik‑kompatibel untuk Arial, Times New Roman, dan Courier New, yang memang dirujuk dokumen yang dibuat di Windows. fonts-noto-cjk meliputi bahasa Cina, Jepang, dan Korea.

Langkah 3 - Resolusi sebuah family alih‑alih menyebutkan satu

Cara portabel untuk memilih font adalah mencoba tanda tangan percobaan per kandidat dan mempertahankan yang pertama tidak melempar:

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

return null;

Deteksi berdasarkan nama berkas adalah jalan pintas yang menggoda namun salah. fonts-noto-cjk Debian menginstal NotoSansCJK-Regular.ttc, yang nama familinya adalah Noto Sans CJK JP. Pencocokan nama berkas melewatkan font yang ada dan mengklaim family yang tidak akan ter‑resolve ketika diberikan ke SignatureFont.

Langkah 4 - Tanda tangani apa yang ter‑resolve, verifikasi apa yang Anda tanda tangani

Family Latin yang ter‑resolve diperlukan; family CJK yang ter‑resolve bersifat opsional dan ketidakhadirannya berarti dilewati, bukan crash:

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);

Kemudian baca kembali berkasnya, karena CJK tanpa font CJK dapat muncul sebagai kotak kosong tanpa mengeluarkan error sama sekali:

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

Perbandingan: Sebelum vs. Sesudah

Dockerfile.nofonts Dockerfile
File font dalam image 0 DejaVu, Liberation, Noto CJK
Tanda tangan teks Latin gagal, keluar 3 tertulis dan dipulihkan saat dibaca kembali
Tanda tangan teks CJK gagal tertulis dan dipulihkan
Kesalahan yang muncul Font <name> was not found none
Perbedaan kode tidak ada - binary yang sama tidak ada - binary yang sama

Baris terakhir adalah intinya. Tidak ada yang berubah dalam aplikasi antara dua run. Repository contoh menyertakan kedua file sehingga perbandingan memerlukan dua perintah docker build alih‑alih mengandalkan dugaan. Simpan varian tanpa font di repository juga setelahnya: itu cara tercepat mereproduksi kegagalan ketika seseorang mengganti image dasar enam bulan ke depan dan tanda tangan tiba‑tiba tidak muncul lagi.

Mengapa tidak langsung menginstal semua font?

Karena ukuran image merupakan kendala nyata dan empat paket di atas sudah mencakup skrip yang paling sering dipakai dokumen. fonts-dejavu-core saja cukup untuk penandatanganan Latin, Greek, dan Cyrillic; Liberation penting ketika dokumen merujuk family Windows dengan nama; Noto CJK adalah yang memang besar dan hanya membayar dirinya sendiri jika Anda menandatangani teks Asia Timur. Instal apa yang dibutuhkan dokumen Anda, lalu verifikasi dengan membaca kembali.

Contoh Dunia Nyata: Pekerja Penandatanganan Batch

Seorang pekerja antrian menandatangani beberapa ribu PDF setiap malam. Dengan resolusi saat startup, ia mencatat satu baris yang menyebutkan family yang akan digunakannya, dan jika tidak ada yang ter‑resolve ia keluar sebelum menyentuh antrian daripada gagal per pesan. Pemeriksaan startup inilah yang mengubah masalah font dari rangkaian pekerjaan gagal menjadi kontainer yang menolak mulai dengan alasan satu baris.

Biaya probing cukup kecil untuk diabaikan saat startup dan terlalu besar untuk diulang per dokumen. Setiap probing adalah tanda tangan nyata yang ditulis ke berkas sementara, sehingga daftar Latin menghabiskan hingga empat probing dan daftar CJK hingga delapan, semuanya pada PDF satu halaman. Resolve sekali, cache dua nama family, dan jalur per‑dokumen tetap persis seperti sebelumnya: bangun opsi, panggil Sign, baca jumlah hasil.

Saya kehilangan satu sore karena versi yang menebak. Ia memindai direktori font, menemukan NotoSansCJK-Regular.ttc, melaporkan CJK tersedia, lalu gagal pada setiap nama family yang saya turunkan dari nama berkas itu. Probing dengan tanda tangan nyata jauh lebih sederhana dan tepat.

Apa Lagi yang Menyebabkan Masalah di Container?

Satu lagi, dan tidak berhubungan dengan font: InvariantGlobalization=true. Ini saran standar untuk memangkas ICU dari image .NET, dan dengan GroupDocs.Signature membuat new Signature(...) pertama melempar CultureNotFoundException: ... en-US is an invalid culture identifier, karena SignatureSettings membangun CultureInfo("en-US"). Biarkan globalisasi tetap aktif dan biarkan ICU tetap berada di dalam image. Halaman system requirements adalah tempat memeriksa dukungan platform sebelum berkomitmen pada image dasar.

Kesimpulan

Layanan penandatanganan yang berfungsi secara lokal namun gagal di Docker hampir selalu kekurangan font, dan solusinya adalah lapisan empat paket ditambah kode yang meresolusi family alih‑alih mengasumsikan satu. Bangun kedua image dari contoh, jalankan berdampingan, dan baca baris [fonts]: seluruh argumen muat dalam satu perbandingan itu.

Sumber Daya Tambahan