💡 Plně funkční příklad je k dispozici na GitHubu:
python-linux-container-pdf-signing
Úvod
Skript funguje lokálně. Zabalíte jej do kontejneru na základě obrazu python:3.11-slim a selže při import groupdocs.signature. Opravíte to a selže znovu při první podpisové operaci. Žádná z chyb neuvádí, co konkrétně chybí.
Podpisování v kontejneru pomocí Pythonu je workflow GroupDocs.Signature, které vyžaduje dva vrstvy provisioning místo jedné: knihovny .NET runtime, na nichž je vazba postavena, a fonty, které musí každá textová podpisová značka použít k vykreslení. Tento tutoriál vytvoří obě vrstvy a poté skript, který za běhu určuje rodinu fontu místo pevného zakódování, takže stejný kód funguje jak v kontejneru, tak na stroji, kde byl napsán.
Proč jsou důležité obě vrstvy
GroupDocs.Signature pro Python je .NET vazba, takže před tím, než jakýkoli import uspěje, musí být přítomny libicu a knihovna kompatibilní s OpenSSL 1.1. To je první vrstva a je dobře zdokumentována v Running in Docker.
Důvod, proč se tyto dvě vrstvy často zaměňují, je ten, že obě selhávají v okamžicích souvisejících s importem a žádná chyba neuvádí svůj původ. Chybějící libssl1.1 vám dá chybu načítače o sdíleném objektu; chybějící font vám dá chybu podpisu zabalenou v proxy výjimce. Žádná z nich neříká „váš základní obraz je příliš malý“, což je ve skutečnosti to, co obě situace znamenají.
Druhá vrstva jsou fonty a to je ta, která lidi překvapuje. python:3.11-slim neobsahuje žádné soubory fontů. GroupDocs.Signature nenahrazuje chybějící rodinu – pokud zadáte rodinu, která není nainstalovaná, vyvolá výjimku a nic se neuloží – a vymazání fontu také není řešení, protože knihovna pak požaduje svůj výchozí font a selže stejným způsobem. Na obrazu bez fontů je textový podpis prostě nemožný.
Požadavky
Python 3.11 (kolo pod CPython 3.14) a groupdocs-signature-net==26.1. Docker, pokud chcete úmyslně vidět oba selhání, což zabere asi deset minut.
Instalace
pip install groupdocs-signature-net==26.1
Krok 1 – Vytvoření .NET vrstvy
libssl1.1 není v distribuci bookworm, takže se získá z pevně určeného Debian snapshotu:
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/*
Klíčové body:
- Tato vrstva umožní pouze import; nic neříká o fontech.
- Připnutí data snapshotu udržuje build reprodukovatelný, i když archiv postupně mizí.
Krok 2 – Vytvoření vrstvy fontů
Čtyři balíčky, udržované jako samostatná vrstva, aby šlo snadno zakomentovat a reprodukovat selhání:
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 je resolver a poskytuje příkaz fc-list. fonts-dejavu-core pokrývá latinskou, řeckou a cyrilickou základnu. fonts-liberation zajišťuje dokumenty, které odkazují na Arial nebo Times New Roman podle názvu. fonts-noto-cjk pokrývá čínštinu, japonštinu a korejštinu.
Krok 3 – Zeptání se knihovny, kterou rodinu může použít
Prohledávání /usr/share/fonts podle názvu souboru se může zdát ekvivalentní, ale není: fonts-noto-cjk nainstaluje NotoSansCJK-Regular.ttc, jehož název rodiny je Noto Sans CJK JP. Přenositelné řešení je provést zkoušku – skutečný podpis do dočasného souboru – a selhání převést na hodnotu:
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
Všimněte si řádku font.size = 10.0. Vazba převádí velikost na .NET float a odmítá int s chybou numeric argument expected, got ‘int’. Protože se to děje uvnitř zkoušky, každá kandidátská rodina selže a výstup vypadá přesně jako obraz bez fontů. Přidal jsem tři balíčky fontů do obrazu, který je už měl, a teprve pak jsem zaznamenal tento problém.
Řešení je pak smyčka:
for candidate in candidates:
if try_family(source_path, candidate) is None:
return candidate
return None
Krok 4 – Podepište to, co bylo vyřešeno, a ověřte, co jste podepsali
Latinská rodina je povinná, CJK rodina volitelná:
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)
Pak ověřte, protože CJK vykreslené jako prázdné rámečky nic nevyvolá:
options = TextVerifyOptions()
options.text = expected_text
options.match_type = gsd.TextMatchType.CONTAINS
options.all_pages = True
result = sign.verify(options)
CONTAINS je úmyslné: v režimu hodnocení knihovna přidá zkušební text na stránku a přesná shoda by označila perfektně dobrý dokument jako neúspěšný.
Co říkají dokumenty, že Python má omezenou podporu Linuxu?
Stránka Running in Docker uvádí Python balíčky připravené pro Linux a vynechává Signature. Na groupdocs-signature-net==26.1 tento příklad podepsal a ověřil uvnitř python:3.11-slim, včetně CJK, po instalaci obou vrstev. Považujte seznam za zastaralý, nikoli za překážku, a ověřte si vlastní verzi před nasazením.
Praktické aplikace
Fakturační služba, která do generovaných PDF přidává řádek schválení, potřebuje přesně toto: .NET vrstvu, jeden latinský font a kontrolu při startu. Tato kontrola promění špatné nasazení v kontejner, který se odmítne spustit, místo fronty faktur, které selhávají tiše po jedné. Portál pro dokumenty, který přijímá jména zákazníků v libovolném skriptu, potřebuje také balíček CJK a ověřovací krok, protože to je jediná věc, která odděluje vykreslený prázdný rámeček od podepsaného jména.
Kam patří kontrola rozlišení
Umístěte ji tam, kde se spouští jednou na proces: volání na úrovni modulu, handler životního cyklu FastAPI, AppConfig.ready v Django, nebo první řádky hlavní funkce workeru. Vrací dvě hodnoty – latinskou a CJK rodinu – a obě patří do startovacího logu vedle počtu fontů.
Toto umístění dělá víc než jen šetří čas zkoušky. Přesouvá selhání z obsluhy požadavku (kde je to problém jednoho zákazníka a stopa, kterou nikdo nečte) na start, kde jde o nasazení, které se nespustilo a někdo už sleduje. Kontejner, který skončí s hláškou „no usable font family, install fonts-dejavu-core“, nepotřebuje žádné ladění.
Řešení běžných problémů
import groupdocs.signature selže
.NET vrstva chybí nebo nebyl během buildu dostupný snapshot repozitář. To je první vrstva a nemá nic společného s fonty. Zkontrolujte log buildu pro krok apt před jakýmkoli kódem podpisu, protože neúspěšné stažení snapshotu neblokuje vytvoření obrazu.
Každý kandidátní font selže, ale fc-list ukazuje fonty
Zkontrolujte, zda font.size není int, než přidáte další balíčky.
Podpis je tam, ale CJK text jsou jen rámečky
Chybí fonts-noto-cjk. Podpis byl vytvořen s rodinou, která nemá glyfy pro tyto kódy, což je důvod, proč existuje ověřovací krok: selže právě v tomto případě, kdy podpis hlásí úspěch.
Co skutečně vypisují oba obrazy
Spusťte oba a přečtěte si první čtyři řádky. Obraz bez fontů hlásí font files on disk: 0, oba řádky rozlišení jako (none), úmyslnou chybu chybějícího fontu a pak končí s kódem 3 a vytištěnou minimální opravou. Provisionovaný obraz hlásí nenulový počet fontů, DejaVu Sans pro latinské a Noto Sans CJK JP pro CJK, dva aplikované podpisy a oba texty ověřené.
Tento dvojice výstupů je artefakt, který stojí za zachování. Vložte jej do svých nasazovacích poznámek a další osoba, která změní základní obraz, bude mít referenci, jak vypadá zdravý kontejner, aniž by musela rozumět fontconfig.
Závěr
Dvě vrstvy a jedna zkouška. Nainstalujte .NET závislosti, nainstalujte alespoň fontconfig a DejaVu, zjistěte rodinu dotazem místo předpokladu a ověřte výstup před tím, než prohlásíte úlohu za dokončenou. Není to mnoho kódu a vše je takové, co je po zpětném pohledu zřejmé a v tracebacku neviditelné. Ukázkové úložiště obsahuje oba Dockerfile, takže rozdíl mezi fungujícím a nefunkčním obrazem je jen jeden build.