💡 Voll funktionsfähiges Beispiel auf GitHub verfügbar:
python-linux-container-pdf-signing

Einführung

Das Skript funktioniert lokal. Sie containerisieren es auf python:3.11-slim und es schlägt beim import groupdocs.signature fehl. Sie beheben das, und es schlägt erneut beim ersten Signaturversuch fehl. Keine der Fehlermeldungen gibt an, was tatsächlich fehlt.

Das Signieren von Containern mit Python ist ein GroupDocs.Signature‑Workflow, der zwei Bereitstellungsschichten statt einer benötigt: die .NET‑Laufzeitbibliotheken, auf denen das Binding aufbaut, und die Schriftarten, mit denen jede Textsignatur gerendert werden muss. Dieses Tutorial erstellt beide Schichten und anschließend das Skript, das zur Laufzeit eine Schriftfamilie ermittelt, anstatt sie fest zu codieren, sodass derselbe Code sowohl im Container als auch auf dem Rechner, auf dem er geschrieben wurde, funktioniert.

Warum beide Schichten wichtig sind

GroupDocs.Signature für Python ist ein .NET‑Binding, daher müssen libicu und eine OpenSSL‑1.1‑kompatible Bibliothek vorhanden sein, bevor irgendein Import gelingt. Das ist Schicht 1 und ist gut dokumentiert in Running in Docker.

Der Grund, warum die beiden Schichten oft miteinander verwechselt werden, liegt darin, dass beide bei import‑nahen Momenten fehlschlagen und keine Fehlermeldung die Ursache benennt. Ein fehlendes libssl1.1 liefert einen Loader‑Fehler über ein Shared‑Object; eine fehlende Schriftart liefert einen Signatur‑Fehler, der in einer Proxy‑Exception verpackt ist. Keine Meldung sagt „Ihr Basis‑Image ist zu klein“, was tatsächlich die Bedeutung beider Fehlermeldungen ist.

Schicht 2 sind die Schriftarten, und hier stoßen die meisten Menschen auf Überraschungen. python:3.11-slim enthält keinerlei Schriftdateien. GroupDocs.Signature substituiert keine fehlende Familie – die Angabe einer nicht installierten Familie löst einen Fehler aus, und es wird nichts geschrieben – und das Entfernen der Schriftart ist ebenfalls kein Work‑around, weil die Bibliothek dann nach ihrer eigenen Standardschrift fragt und exakt gleich fehlschlägt. Auf einem bildlosen Image ist eine Textsignatur schlicht unmöglich.

Voraussetzungen

Python 3.11 (die Wheels gelten bis unter CPython 3.14) und groupdocs-signature-net==26.1. Docker, falls Sie beide Fehlermeldungen bewusst reproduzieren möchten – das dauert etwa zehn Minuten.

Installation

pip install groupdocs-signature-net==26.1

Schritt 1 – .NET‑Schicht erstellen

libssl1.1 ist in bookworm nicht enthalten, daher wird es aus einem festgelegten Debian‑Snapshot bezogen:

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

Wichtige Punkte:

  • Diese Schicht sorgt ausschließlich dafür, dass der Import funktioniert; sie sagt nichts über Schriftarten aus.
  • Das Festlegen des Snapshot‑Datums macht den Build reproduzierbar, wenn das Archiv weiterentwickelt wird.

Schritt 2 – Schriftart‑Schicht erstellen

Vier Pakete, als eigene Schicht gehalten, damit sie auskommentiert werden können, um den Fehler zu reproduzieren:

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 ist der Resolver und liefert fc-list. fonts-dejavu-core deckt das Minimum für Latein, Griechisch und Kyrillisch ab. fonts-liberation sorgt für Dokumente, die Arial oder Times New Roman per Namen referenzieren. fonts-noto-cjk deckt Chinesisch, Japanisch und Koreanisch ab.

Schritt 3 – Bibliothek fragen, welche Familie verwendet werden kann

Das Durchsuchen von /usr/share/fonts nach einem Dateinamen wirkt gleichwertig, ist es aber nicht: fonts-noto-cjk installiert NotoSansCJK-Regular.ttc, dessen Familienname Noto Sans CJK JP lautet. Die portable Lösung ist ein Probe‑Signatur‑Durchlauf in eine temporäre Datei, wobei der Fehlversuch in einen Rückgabewert umgewandelt wird:

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

Achten Sie genau auf font.size = 10.0. Das Binding wandelt die Größe in einen .NET‑float um und verwirft einen int mit der Meldung numeric argument expected, got ‘int’. Da dies innerhalb der Probe geschieht, schlägt jede Kandidatenfamilie fehl und das Ergebnis sieht exakt wie ein bildloses Image aus. Ich habe drei Schriftpakete zu einem Image hinzugefügt, das bereits alle enthielt, bevor ich den wörtlichen Fehler bemerkte.

Die Auflösung erfolgt dann in einer Schleife:

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

Schritt 4 – Signieren, was aufgelöst wurde, und überprüfen, was Sie signiert haben

Die lateinische Familie ist zwingend erforderlich, die CJK‑Familie optional:

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)

Anschließend verifizieren wir, weil CJK‑Zeichen, die als leere Kästchen gerendert werden, sonst keinen Fehler auslösen:

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

CONTAINS ist bewusst gewählt: Im Evaluierungsmodus fügt die Bibliothek Testtext zur Seite hinzu, und ein exakter Treffer würde ein vollkommen korrektes Dokument fälschlicherweise als fehlgeschlagen melden.

Was ist mit der Dokumentation, die besagt, dass Python nur begrenzte Linux‑Unterstützung hat?

Die Seite Running in Docker listet Linux‑bereite Python‑Pakete auf und lässt Signature weg. Mit groupdocs-signature-net==26.1 hat dieses Beispiel innerhalb von python:3.11-slim sowohl signiert als auch verifiziert, inklusive CJK, nachdem beide Schichten installiert waren. Betrachten Sie die Liste als veraltet, nicht als Blocker, und prüfen Sie mit Ihrer eigenen Version, bevor Sie eine Bereitstellung festlegen.

Praktische Anwendungsbeispiele

Ein Rechnungsservice, der eine Genehmigungszeile auf erzeugte PDFs stempelt, benötigt genau das: die .NET‑Schicht, eine lateinische Schriftart und eine Start‑Auflösungsprüfung. Diese Prüfung verhindert, dass ein fehlerhaftes Deployment zu einem Container führt, der nicht startet, anstatt dass ein Stapel Rechnungen stillschweigend einzeln fehlschlägt. Ein Dokumenten‑Portal, das Kundennamen in beliebigen Schriftsystemen akzeptiert, benötigt zusätzlich das CJK‑Paket sowie den Verifizierungsschritt, weil dies das Einzige ist, das zwischen einer gerenderten Box und einem signierten Namen steht.

Wo die Auflösungsprüfung hingehört

Platzieren Sie sie dort, wo sie einmal pro Prozess ausgeführt wird: ein Aufruf auf Modulebene, ein FastAPI‑Lifespan‑Handler, ein Django‑AppConfig.ready oder die ersten Zeilen der Hauptfunktion eines Workers. Zwei Werte resultieren daraus – die lateinische und die CJK‑Familie – und beide gehören in das Start‑Log neben der Schrift‑Anzahl.

Diese Platzierung spart nicht nur Probezeit, sondern verlagert den Fehler von der Anfrage‑Verarbeitung (ein Kundenproblem mit einem Stack‑Trace, den niemand liest) zum Start, wo ein Deployment einfach nicht hochfährt und jemand bereits darüber wacht. Ein Container, der mit „no usable font family, install fonts-dejavu-core“ beendet wird, benötigt keinerlei Debugging.

Häufige Probleme beheben

import groupdocs.signature schlägt fehl
Die .NET‑Schicht fehlt oder das Snapshot‑Repository war während des Builds nicht erreichbar. Das ist Schicht 1 und hat nichts mit Schriftarten zu tun. Prüfen Sie das Build‑Log für den apt‑Schritt, bevor Sie Code zur Signatur ändern, denn ein fehlgeschlagener Snapshot‑Abruf stoppt den Image‑Build nicht.

Jede Kandidaten‑Schriftart schlägt fehl, aber fc-list zeigt Schriftarten
Überprüfen Sie, ob font.size als int angegeben ist, bevor Sie weitere Pakete hinzufügen.

Die Signatur ist vorhanden, aber der CJK‑Text erscheint als Kästchen
fonts-noto-cjk fehlt. Die Signatur wurde mit einer Familie geschrieben, die für diese Code‑Points keine Glyphen besitzt – genau dafür gibt es den Verifizierungsschritt: Er schlägt in diesem Fall fehl, obwohl das Signieren Erfolg meldete.

Was die beiden Images tatsächlich ausgeben

Führen Sie beide aus und lesen Sie die ersten vier Zeilen. Das bildlose Image meldet font files on disk: 0, beide Auflösungszeilen als (none), den absichtlich fehlenden‑Schrift‑Fehler und beendet sich mit Exit‑Code 3, wobei die minimale Korrektur ausgegeben wird. Das bereitgestellte Image meldet eine von Null verschiedene Schrift‑Anzahl, DejaVu Sans für Latein und Noto Sans CJK JP für CJK, zwei angewendete Signaturen und beide Texte verifiziert.

Dieses Ausgabe‑Paar ist das Artefakt, das Sie behalten sollten. Fügen Sie es Ihren Deploy‑Notizen hinzu, und die nächste Person, die das Basis‑Image ändert, hat eine Referenz dafür, wie ein gesunder Container aussieht, ohne überhaupt fontconfig verstehen zu müssen.

Fazit

Zwei Schichten und ein Probe‑Durchlauf. Installieren Sie die .NET‑Abhängigkeiten, installieren Sie mindestens fontconfig und DejaVu, ermitteln Sie die Familie, indem Sie nachfragen statt anzunehmen, und verifizieren Sie das Ergebnis, bevor Sie den Job als erledigt markieren. Der Code ist minimal, und das Ganze ist das, was im Nachhinein offensichtlich, im Traceback aber unsichtbar ist. Das Beispiel‑Repository liefert beide Dockerfiles, sodass der Unterschied zwischen einem funktionierenden und einem defekten Image nur einen Build‑Schritt entfernt ist.

Weitere Ressourcen