💡 Ví dụ hoạt động đầy đủ có trên GitHub:
python-linux-container-pdf-signing
Giới thiệu
Kịch bản chạy được trên máy cục bộ. Khi bạn container hoá nó trên python:3.11-slim, nó sẽ thất bại ở import groupdocs.signature. Bạn sửa lỗi đó, nhưng lại thất bại lần nữa ở chữ ký đầu tiên. Cả hai lỗi đều không chỉ ra nguyên nhân thực sự thiếu gì.
Ký tên trong container bằng Python là một quy trình của GroupDocs.Signature cần hai lớp cung cấp thay vì một: các thư viện runtime .NET mà binding dựa trên, và các phông chữ mà mọi chữ ký văn bản phải dùng để hiển thị. Hướng dẫn này xây dựng cả hai lớp, sau đó viết script để xác định họ phông chữ tại thời gian chạy thay vì mã cứng, vì vậy cùng một đoạn mã sẽ hoạt động trong container và trên máy bạn viết nó.
Tại sao cả hai lớp đều quan trọng
GroupDocs.Signature cho Python là một binding .NET, vì vậy libicu và một thư viện tương thích OpenSSL 1.1 phải tồn tại trước khi bất kỳ import nào thành công. Đó là lớp thứ nhất, và nó được mô tả chi tiết trong Running in Docker.
Nguyên nhân hai lớp bị nhầm lẫn là vì cả hai đều thất bại tại các thời điểm gần import và lỗi không nêu rõ nguyên nhân. Thiếu libssl1.1 sẽ cho bạn lỗi loader về một shared object; thiếu phông chữ sẽ cho bạn lỗi ký được bao bọc trong một proxy exception. Cả hai đều không nói “image cơ sở của bạn quá nhỏ”, mà thực tế là chúng đang muốn nói điều đó.
Lớp thứ hai là phông chữ, và đây là phần khiến mọi người ngạc nhiên. python:3.11-slim không chứa bất kỳ tệp phông chữ nào. GroupDocs.Signature không tự thay thế họ phông chữ bị thiếu – việc đặt tên một họ không được cài đặt sẽ gây lỗi, và không có gì được ghi – và việc xóa phông chữ cũng không phải là giải pháp tạm thời, vì thư viện sau đó sẽ yêu cầu mặc định của nó và thất bại tương tự. Trên một image không có phông, một chữ ký văn bản đơn giản là không thể thực hiện.
Yêu cầu trước
Python 3.11 (bánh xe dưới CPython 3.14) và groupdocs-signature-net==26.1. Docker nếu bạn muốn thấy cả hai lỗi một cách có chủ đích, việc này chỉ mất khoảng mười phút.
Cài đặt
pip install groupdocs-signature-net==26.1
Bước 1 – Xây dựng lớp .NET
libssl1.1 không có trong bookworm, vì vậy nó được lấy từ một snapshot Debian đã được cố định:
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/*
Các điểm chính:
- Lớp này chỉ làm cho việc import hoạt động; nó không liên quan gì đến phông chữ.
- Định ngày snapshot giúp quá trình build tái tạo được khi kho lưu trữ thay đổi.
Bước 2 – Xây dựng lớp phông chữ
Bốn gói, được giữ riêng thành một lớp để có thể bình luận ra và tái tạo lỗi:
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 là trình giải quyết và cung cấp cho bạn fc-list. fonts-dejavu-core là bộ phông Latin, Greek và Cyrillic tối thiểu. fonts-liberation hỗ trợ các tài liệu tham chiếu Arial hoặc Times New Roman theo tên. fonts-noto-cjk bao gồm Chinese, Japanese và Korean.
Bước 3 – Yêu cầu thư viện cho biết họ phông nào có thể dùng
Việc quét /usr/share/fonts để tìm tên tệp trông có vẻ tương đương nhưng không phải: fonts-noto-cjk cài đặt NotoSansCJK-Regular.ttc, có họ là Noto Sans CJK JP. Câu trả lời di động là thực hiện một probe – một chữ ký thực tế vào một tệp tạm – và chuyển lỗi thành một giá trị:
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
Hãy chú ý font.size = 10.0. Binding sẽ chuyển kích thước thành một .NET float và từ chối một int với thông báo numeric argument expected, got 'int'. Vì điều này xảy ra trong probe, mọi họ phông chữ ứng cử đều thất bại và kết quả trông giống như một image không có phông. Tôi đã thêm ba gói phông vào một image đã có sẵn chúng trước khi nhận ra lỗi này.
Giải pháp sau đó là một vòng lặp:
for candidate in candidates:
if try_family(source_path, candidate) is None:
return candidate
return None
Bước 4 – Ký những gì đã được xác định, xác minh những gì bạn đã ký
Họ phông Latin là bắt buộc, họ CJK là tùy chọn:
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)
Sau đó xác minh, vì CJK hiển thị dưới dạng các hộp trống sẽ không gây lỗi:
options = TextVerifyOptions()
options.text = expected_text
options.match_type = gsd.TextMatchType.CONTAINS
options.all_pages = True
result = sign.verify(options)
CONTAINS được dùng cố ý: ở chế độ đánh giá, thư viện sẽ thêm văn bản thử nghiệm vào trang, và một khớp chính xác sẽ báo cáo một tài liệu hoàn toàn tốt là thất bại.
Tài liệu nói gì về việc Python hỗ trợ Linux hạn chế?
Trang Running in Docker liệt kê các gói Python đã sẵn sàng cho Linux và không đề cập đến Signature. Với groupdocs-signature-net==26.1 mẫu này đã ký và xác minh thành công trong python:3.11-slim, bao gồm CJK, khi cả hai lớp đã được cài đặt. Hãy xem danh sách đó như đã lỗi thời chứ không phải là rào cản, và xác nhận với phiên bản của bạn trước khi triển khai.
Ứng dụng thực tế
Một dịch vụ lập hoá đơn cần dán một dòng phê duyệt lên các PDF được tạo ra thì cần chính xác những gì này: lớp .NET, một phông Latin, và một kiểm tra giải quyết khi khởi động. Kiểm tra này là thứ biến một triển khai sai thành một container không khởi động, thay vì một hàng đợi hoá đơn thất bại âm thầm từng cái một. Một cổng tài liệu cho phép khách hàng nhập tên bằng bất kỳ chữ viết nào cũng cần gói CJK, cộng thêm bước xác minh, vì đó là cách duy nhất ngăn chặn việc hiển thị hộp trống thay vì tên đã ký.
Nơi đặt kiểm tra giải quyết
Đặt nó ở bất kỳ nơi nào chạy một lần cho mỗi tiến trình: lời gọi ở mức module, một handler vòng đời FastAPI, AppConfig.ready của Django, hoặc những dòng đầu tiên của worker. Hai giá trị trả về là họ phông Latin và họ phông CJK, và cả hai nên được ghi vào log khởi động bên cạnh số lượng phông.
Việc đặt ở đó không chỉ tiết kiệm thời gian probe. Nó chuyển lỗi từ xử lý yêu cầu (vấn đề của một khách hàng và một stack trace ít ai đọc) sang thời điểm khởi động, nơi lỗi trở thành một container không khởi động và ai đó đã đang theo dõi. Một container thoát với thông báo “no usable font family, install fonts-dejavu-core” không cần bất kỳ việc gỡ lỗi nào.
Khắc phục các vấn đề thường gặp
import groupdocs.signature thất bại
Lớp .NET còn thiếu hoặc kho snapshot không thể truy cập trong quá trình build. Đây là lớp thứ nhất và không liên quan tới phông chữ. Kiểm tra log build ở bước apt trước khi chạm vào bất kỳ mã ký nào, vì việc fetch snapshot thất bại không ngăn image được xây dựng.
Mọi phông chữ ứng cử đều thất bại, nhưng fc-list hiện có phông
Kiểm tra font.size xem có phải là int không trước khi thêm gói khác.
Chữ ký hiện hữu nhưng văn bản CJK chỉ là các hộp
fonts-noto-cjk bị thiếu. Chữ ký đã được ghi bằng một họ không có glyph cho các mã điểm đó, vì vậy bước xác minh tồn tại: nó sẽ thất bại trong trường hợp này, ngay cả khi quá trình ký báo thành công.
Hai image thực tế in ra gì
Chạy cả hai và đọc bốn dòng đầu tiên. Image không có phông sẽ báo font files on disk: 0, cả hai dòng giải quyết sẽ là (none), lỗi thiếu phông dự định, và sau đó thoát với mã 3 cùng bản sửa tối thiểu được in. Image đã được cung cấp sẽ báo số phông không bằng 0, DejaVu Sans cho Latin và Noto Sans CJK JP cho CJK, hai chữ ký được áp dụng, và cả hai văn bản đều được xác minh.
Cặp kết quả này là tài liệu đáng giữ. Dán nó vào ghi chú triển khai của bạn và người tiếp theo thay đổi image cơ sở sẽ có tham chiếu về một container khỏe mạnh, mà không cần hiểu sâu về fontconfig.
Kết luận
Hai lớp và một probe. Cài đặt các phụ thuộc .NET, cài đặt ít nhất fontconfig và DejaVu, giải quyết họ phông bằng cách hỏi thay vì giả định, và xác minh kết quả trước khi coi công việc đã xong. Tất cả đều là mã ít, và đều là những điều hiển nhiên khi nhìn lại nhưng vô hình trong traceback. Kho mẫu cung cấp cả hai Dockerfile, vì vậy sự khác nhau giữa một image hoạt động và một image bị hỏng chỉ cách nhau một lần build.