💡 Contoh lengkap yang berfungsi tersedia di GitHub:
python-linux-container-pdf-signing
Pendahuluan
Skrip ini berjalan secara lokal. Anda mengkontainerisasikannya pada python:3.11-slim, dan ia gagal pada import groupdocs.signature. Anda memperbaikinya, dan ia kembali gagal pada tanda tangan pertama. Tidak ada error yang menyebutkan apa yang sebenarnya hilang.
Penandatanganan kontainer dengan Python adalah alur kerja GroupDocs.Signature yang memerlukan dua lapisan penyediaan, bukan satu: perpustakaan runtime .NET tempat binding dibangun, dan font yang harus digunakan setiap tanda tangan teks untuk dirender. Tutorial ini membangun keduanya, kemudian skrip yang menentukan keluarga font pada waktu berjalan alih‑alih mengkodekannya secara tetap, sehingga kode yang sama berfungsi di dalam kontainer dan pada mesin tempat Anda menulisnya.
Mengapa Kedua Lapisan Penting
GroupDocs.Signature untuk Python adalah binding .NET, sehingga libicu dan pustaka yang kompatibel dengan OpenSSL 1.1 harus ada sebelum impor apa pun berhasil. Itu adalah lapisan pertama, dan sudah didokumentasikan dengan baik di Running in Docker.
Alasan kedua lapisan sering dicampur adalah karena keduanya gagal pada momen yang berdekatan dengan impor dan tidak ada error yang menyebutkan penyebabnya. libssl1.1 yang hilang memberi Anda error loader tentang objek bersama; font yang hilang memberi Anda error penandatanganan yang dibungkus dalam pengecualian proxy. Tidak ada yang mengatakan “gambar dasar Anda terlalu kecil”, padahal itulah yang sebenarnya terjadi.
Lapisan dua adalah font, dan inilah yang mengejutkan orang. python:3.11-slim tidak berisi file font sama sekali. GroupDocs.Signature tidak menggantikan keluarga yang hilang — menyebutkan satu yang tidak terpasang akan memunculkan exception, dan tidak ada apa‑apa yang ditulis — dan mengosongkan font bukan solusi, karena perpustakaan kemudian meminta defaultnya sendiri dan gagal dengan cara yang sama. Pada gambar tanpa font, tanda tangan teks secara sederhana tidak mungkin.
Prasyarat
Python 3.11 (wheel di bawah CPython 3.14) dan groupdocs-signature-net==26.1. Docker jika Anda ingin melihat kedua kegagalan secara sengaja, yang memakan waktu sekitar sepuluh menit.
Instalasi
pip install groupdocs-signature-net==26.1
Langkah 1 - Bangun lapisan .NET
libssl1.1 tidak ada di bookworm, sehingga diambil dari snapshot Debian yang dipatok:
ENV SNAPSHOT_DATE=20220328T000000Z
RUN echo "deb [trusted=yes] http://snapshot.debian.org/archive/debian/${SNAPSHOT_DATE} bullseye main" \
> /etc/apt/sources.list.d/debian-archive.list \
&& apt-get -o Acquire::Check-Valid-Until=false update \
&& apt-get install -y --no-install-recommends \
libicu67 \
libssl1.1 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
Poin penting:
- Lapisan ini hanya membuat impor berhasil; tidak ada hubungannya dengan font.
- Mematok tanggal snapshot menjaga agar build dapat direproduksi ketika arsip berubah.
Langkah 2 - Bangun lapisan font
Empat paket, dipisahkan sebagai lapisan tersendiri sehingga dapat dikomentari untuk mereproduksi kegagalan:
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. fonts-dejavu-core menyediakan minimum Latin, Yunani, dan Kiril. fonts-liberation meliputi dokumen yang menyebut Arial atau Times New Roman secara eksplisit. fonts-noto-cjk meliputi bahasa Cina, Jepang, dan Korea.
Langkah 3 - Tanyakan pada perpustakaan keluarga font mana yang dapat dipakai
Memindai /usr/share/fonts untuk nama file tampak setara, tetapi tidak: fonts-noto-cjk memasang NotoSansCJK-Regular.ttc, yang nama keluarganya adalah Noto Sans CJK JP. Jawaban portabel adalah probe — tanda tangan nyata ke file sementara — dengan kegagalan yang diubah menjadi nilai:
with signature.Signature(source_path) as sign:
options = TextSignOptions()
options.text = "probe"
options.left = 10
options.top = 10
options.width = 60
options.height = 20
font = SignatureFont()
font.family_name = family_name
font.size = 10.0
options.font = font
sign.sign(scratch, [options])
return None
Perhatikan font.size = 10.0. Binding memetakan ukuran ke .NET float dan menolak int dengan pesan numeric argument expected, got 'int'. Karena hal itu terjadi di dalam probe, setiap keluarga kandidat gagal dan outputnya tampak persis seperti gambar tanpa font. Saya menambahkan tiga paket font ke sebuah image yang sebenarnya sudah memiliki semuanya sebelum menyadari hal tersebut.
Penyelesaiannya kemudian berupa loop:
for candidate in candidates:
if try_family(source_path, candidate) is None:
return candidate
return None
Langkah 4 - Tanda tangani apa yang telah terdeteksi, verifikasi apa yang Anda tanda tangani
Keluarga Latin wajib, keluarga CJK opsional:
with signature.Signature(source_path) as sign:
options = [build_text_options(LATIN_TEXT, latin_family, 50)]
if cjk_family:
options.append(build_text_options(CJK_TEXT, cjk_family, 120))
result = sign.sign(output_path, options)
return len(result.succeeded)
Kemudian verifikasi, karena CJK yang dirender sebagai kotak kosong tidak menimbulkan error:
options = TextVerifyOptions()
options.text = expected_text
options.match_type = gsd.TextMatchType.CONTAINS
options.all_pages = True
result = sign.verify(options)
CONTAINS dipilih dengan sengaja: dalam mode evaluasi perpustakaan menambahkan teks percobaan ke halaman, dan pencocokan tepat akan melaporkan dokumen yang sebenarnya baik sebagai gagal.
Bagaimana dengan dokumentasi yang menyatakan Python memiliki dukungan Linux terbatas?
Halaman Running in Docker mencantumkan paket Python yang siap Linux dan tidak menyertakan Signature. Pada groupdocs-signature-net==26.1 contoh ini berhasil menandatangani dan memverifikasi di dalam python:3.11-slim, termasuk CJK, dengan kedua lapisan terpasang. Anggap daftar tersebut sudah usang, bukan penghalang, dan konfirmasikan dengan versi Anda sendiri sebelum berkomitmen pada deployment.
Aplikasi Dunia Nyata
Layanan faktur yang menempelkan baris persetujuan pada PDF yang dihasilkan membutuhkan hal ini: lapisan .NET, satu font Latin, dan pemeriksaan resolusi saat startup. Pemeriksaan itulah yang mengubah deployment buruk menjadi kontainer yang menolak untuk memulai, alih‑alih antrean faktur yang gagal secara diam‑diam satu per satu. Portal dokumen yang menerima nama pelanggan dalam skrip apa pun juga memerlukan paket CJK, plus langkah verifikasi, karena itu satu‑satunya hal yang memisahkan antara kotak kosong yang dirender dan nama yang ditandatangani.
Di Mana Pemeriksaan Resolusi Harus Ditempatkan
Letakkan di mana saja yang dijalankan sekali per proses: panggilan level modul, handler FastAPI lifespan, AppConfig.ready Django, atau baris pertama fungsi utama pekerja. Dua nilai keluar darinya, keluarga Latin dan keluarga CJK, dan keduanya harus dicatat di log startup bersamaan dengan jumlah font.
Penempatan itu lebih dari sekadar menghemat waktu probe. Ia memindahkan kegagalan dari penanganan permintaan—yang menjadi masalah satu pelanggan dan jejak stack yang tidak dibaca—ke startup, di mana kegagalan menjadi deployment yang tidak berhasil dan seseorang sudah memperhatikannya. Kontainer yang keluar dengan pesan “no usable font family, install fonts-dejavu-core” tidak memerlukan debugging sama sekali.
Memecahkan Masalah Umum
import groupdocs.signature gagal
Lapisan .NET tidak ada atau repositori snapshot tidak dapat dijangkau selama build. Ini adalah lapisan pertama, dan tidak ada hubungannya dengan font. Periksa log build untuk langkah apt sebelum menyentuh kode penandatanganan apa pun, karena kegagalan pengambilan snapshot tidak menghentikan pembuatan image.
Setiap font kandidat gagal, tetapi fc-list menampilkan font
Periksa font.size apakah berupa int sebelum menambahkan paket lain.
Tanda tangan ada tetapi teks CJK berupa kotak
fonts-noto-cjk tidak terpasang. Tanda tangan ditulis dengan keluarga yang tidak memiliki glyph untuk titik kode tersebut, itulah mengapa langkah verifikasi ada: ia gagal tepat pada kasus ini, di mana penandatanganan melaporkan sukses.
Apa yang Sebenarnya Dicetak Kedua Image
Jalankan keduanya dan baca empat baris pertama. Image tanpa font melaporkan font files on disk: 0, kedua baris resolusi sebagai (none), error font yang sengaja dihilangkan, lalu keluar dengan kode 3 bersama perbaikan minimum yang dicetak. Image yang dipasok melaporkan jumlah font bukan nol, DejaVu Sans untuk Latin dan Noto Sans CJK JP untuk CJK, dua tanda tangan diterapkan, dan kedua teks diverifikasi.
Pasangan output itu adalah artefak yang patut disimpan. Tempelkan ke catatan deployment Anda dan orang berikutnya yang mengubah base image akan memiliki referensi tentang seperti apa kontainer yang sehat, tanpa harus memahami fontconfig sama sekali.
Kesimpulan
Dua lapisan dan satu probe. Instal dependensi .NET, instal setidaknya fontconfig dan DejaVu, resolusi keluarga dengan menanyakan alih‑alih mengasumsikan, dan verifikasi output sebelum menganggap pekerjaan selesai. Semua itu bukan banyak kode, dan semuanya merupakan hal yang jelas setelah melihat kembali namun tak terlihat dalam traceback. Repository contoh menyediakan kedua Dockerfile, sehingga perbedaan antara image yang berfungsi dan yang rusak hanya satu build.