💡 Pełny działający przykład dostępny na GitHubie:
python-linux-container-pdf-signing

Wprowadzenie

Skrypt działa lokalnie. Konteneryzujesz go na obrazie python:3.11-slim i pojawia się błąd przy import groupdocs.signature. Naprawiasz to, a błąd pojawia się ponownie przy pierwszym podpisie. Żaden z błędów nie wskazuje, co faktycznie brakuje.

Podpisywanie w kontenerze przy użyciu Pythona to przepływ pracy GroupDocs.Signature, który wymaga dwóch warstw provisioningowych, a nie jednej: bibliotek środowiska uruchomieniowego .NET, na których oparta jest powiązana biblioteka, oraz czcionek, których musi używać każdy podpis tekstowy. Ten samouczek buduje obie warstwy, a następnie skrypt, który w czasie działania określa rodzinę czcionek zamiast twardo kodować jedną, tak aby ten sam kod działał zarówno w kontenerze, jak i na maszynie, na której go napisałeś.

Dlaczego obie warstwy mają znaczenie

GroupDocs.Signature dla Pythona jest powiązaniem .NET, więc przed udanym importem muszą istnieć libicu oraz biblioteka zgodna z OpenSSL 1.1. To jest warstwa pierwsza i jest dobrze udokumentowana w Running in Docker.

Powodem, dla którego dwie warstwy są mylone, jest to, że obie zawodzą w momentach bliskich importowi i żaden błąd nie podaje przyczyny. Brak libssl1.1 generuje błąd loadera dotyczący obiektu współdzielonego; brak czcionki powoduje błąd podpisu opakowany w wyjątek proxy. Żaden z nich nie mówi „twój obraz bazowy jest za mały”, co w rzeczywistości oznacza oba te przypadki.

Warstwa druga to czcionki i to ona zaskakuje ludzi. python:3.11-slim nie zawiera żadnych plików czcionek. GroupDocs.Signature nie podmienia brakującej rodziny – podanie nazwy niezainstalowanej czcionki powoduje wyjątek, a nic nie zostaje zapisane – i usunięcie czcionki nie jest obejściem, ponieważ biblioteka wtedy żąda własnej domyślnej i ponownie kończy się niepowodzeniem. Na obrazie bez czcionek podpis tekstowy jest po prostu niemożliwy.

Wymagania wstępne

Python 3.11 (koło koła poniżej CPython 3.14) oraz groupdocs-signature-net==26.1. Docker, jeśli chcesz celowo zobaczyć oba błędy – zajmuje to około dziesięciu minut.

Instalacja

pip install groupdocs-signature-net==26.1

Krok 1 – Zbuduj warstwę .NET

libssl1.1 nie znajduje się w bookworm, więc pochodzi z przypiętego migawki Debiana:

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

Kluczowe punkty:

  • Ta warstwa jedynie umożliwia import; nie ma nic wspólnego z czcionkami.
  • Przypięcie daty migawki zapewnia powtarzalność budowy, gdy archiwum się zmienia.

Krok 2 – Zbuduj warstwę czcionek

Cztery pakiety, utrzymane jako osobna warstwa, aby można było je zakomentować i odtworzyć błąd:

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 jest rozwiązywaczem i dostarcza fc-list. fonts-dejavu-core zapewnia minimum dla łacińskiego, greckiego i cyrylicy. fonts-liberation obsługuje dokumenty odwołujące się do Arial lub Times New Roman po nazwie. fonts-noto-cjk obejmuje chiński, japoński i koreański.

Krok 3 – Zapytaj bibliotekę, której rodziny czcionek może użyć

Skanowanie /usr/share/fonts pod kątem nazwy pliku wydaje się równoważne, ale nie jest: fonts-noto-cjk instalują NotoSansCJK-Regular.ttc, którego nazwa rodziny to Noto Sans CJK JP. Przenośną odpowiedzią jest sondowanie – prawdziwy podpis w pliku tymczasowym – z przekształceniem niepowodzenia w wartość:

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

Zwróć uwagę na font.size = 10.0. Powiązanie mapuje rozmiar na .NET float i odrzuca int z komunikatem numeric argument expected, got 'int'. Ponieważ dzieje się to wewnątrz sondy, każda kandydacka rodzina kończy się niepowodzeniem i wynik wygląda dokładnie tak, jakby obraz nie miał czcionek. Dodałem trzy pakiety czcionek do obrazu, który już je miał, zanim zauważyłem dosłowny błąd.

Rozwiązanie to pętla:

for candidate in candidates:
    if try_family(source_path, candidate) is None:
        return candidate
return None

Krok 4 – Podpisz to, co zostało rozwiązane, zweryfikuj to, co podpisałeś

Rodzina łacińska jest wymagana, rodzina CJK opcjonalna:

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)

Następnie weryfikacja, ponieważ CJK renderowane jako puste kwadraty nie generuje błędu:

options = TextVerifyOptions()
options.text = expected_text
options.match_type = gsd.TextMatchType.CONTAINS
options.all_pages = True
result = sign.verify(options)

CONTAINS jest celowe: w trybie oceny biblioteka dodaje tekst próbny do strony, a dokładne dopasowanie zgłosiłoby poprawny dokument jako nieudany.

Co z dokumentacją mówiącą, że Python ma ograniczone wsparcie na Linuksie?

Strona Running in Docker wymienia pakiety Pythona gotowe na Linux i pomija Signature. W wersji groupdocs-signature-net==26.1 ten przykład został podpisany i zweryfikowany wewnątrz python:3.11-slim, w tym CJK, po zainstalowaniu obu warstw. Traktuj tę listę jako przestarzałą, a nie jako blokadę, i potwierdź ją własną wersją przed wdrożeniem.

Zastosowania w rzeczywistym świecie

Usługa fakturowania, która nakłada linię zatwierdzenia na generowane PDF‑y, potrzebuje dokładnie tego: warstwy .NET, jednej łacińskiej czcionki i sprawdzenia rozwiązywania przy starcie. To sprawdzenie zamienia nieudaną implementację w kontener, który odmawia uruchomienia, zamiast w kolejkę faktur, które cicho zawodzą po jednej. Portal dokumentów przyjmujący nazwiska klientów w dowolnym piśmie potrzebuje także pakietu CJK oraz kroku weryfikacji, ponieważ to jedyne, co oddziela pustą ramkę od podpisanego imienia.

Gdzie powinien znajdować się test rozwiązywania

Umieść go w miejscu, które uruchamia się raz na proces: wywołanie na poziomie modułu, handler życia FastAPI, AppConfig.ready w Django lub pierwsze linie main pracownika. Zwraca dwie wartości – rodzinę łacińską i rodzinę CJK – i obie powinny trafić do logu startowego obok liczby czcionek.

Takie umiejscowienie robi więcej niż oszczędza czas sondy. Przenosi niepowodzenie z obsługi żądania (gdzie jest to problem jednego klienta i stos śladu, którego nikt nie czyta) do startu, gdzie jest to wdrożenie, które nie wystartowało, a ktoś już to obserwuje. Kontener, który kończy działanie komunikatem „no usable font family, install fonts-dejavu-core”, nie wymaga żadnego debugowania.

Rozwiązywanie typowych problemów

import groupdocs.signature nie działa
Warstwa .NET jest nieobecna lub repozytorium migawki było nieosiągalne podczas budowy. To warstwa pierwsza i nie ma nic wspólnego z czcionkami. Sprawdź log budowania pod kątem kroku apt przed dotknięciem jakiegokolwiek kodu podpisującego, ponieważ nieudane pobranie migawki nie zatrzymuje budowy obrazu.

Każda kandydacka czcionka zawodzi, ale fc-list pokazuje czcionki
Sprawdź, czy font.size nie jest typu int przed dodaniem kolejnych pakietów.

Podpis jest widoczny, ale tekst CJK to kwadraty
Brakuje fonts-noto-cjk. Podpis został zapisany z rodziną, która nie ma glifów dla tych punktów kodowych, dlatego istnieje krok weryfikacji: on właśnie wykrywa ten przypadek, w którym podpis zgłasza sukces, a w rzeczywistości nie ma widocznego tekstu.

Co faktycznie wypisują dwa obrazy

Uruchom oba i przeczytaj pierwsze cztery linie. Obraz bez czcionek raportuje font files on disk: 0, oba wiersze rozwiązywania jako (none), celowy błąd brakującej czcionki i kończy się kodem wyjścia 3 z wypisanym minimalnym naprawieniem. Obraz z provisioningiem zgłasza niezerową liczbę czcionek, DejaVu Sans dla łacińskiego i Noto Sans CJK JP dla CJK, dwa zastosowane podpisy i oba teksty zweryfikowane.

Ten zestaw wyjść jest artefaktem wartym zachowania. Wklej go do notatek wdrożeniowych, a kolejna osoba zmieniająca obraz bazowy będzie miała odniesienie, jak wygląda zdrowy kontener, bez konieczności rozumienia fontconfig.

Zakończenie

Dwie warstwy i jedna sonda. Zainstaluj zależności .NET, przynajmniej fontconfig i DejaVu, rozwiąż rodzinę pytając, a nie zakładając, i zweryfikuj wynik przed uznaniem zadania za zakończone. To niewiele kodu, a wszystko to jest tym, co po fakcie wydaje się oczywiste, a w śladowym komunikacie niewidoczne. Repozytorium przykładu dostarcza oba Dockerfile, więc różnica między działającym a zepsutym obrazem to jedynie jeden build.

Dodatkowe zasoby