Container font provisioning is a GroupDocs.Signature requirement for Python via .NET that decides whether a text signature can be applied inside a Linux image at all. A PDF text signature needs the font family it names, and the library does not substitute a missing family: it raises Font <name> was not found, and no document is written. Clearing the font does not help, because the library then requests its own default - PDF text and digital signatures default to Times New Roman and Arial - and fails the same way when those are missing.
These four tutorials build the working version in the order a real deployment hits the problems: get the image to run the binding at all, find out which fonts exist, sign with a family that resolves, and verify the result. Every Python example on this page is a complete script.
What This Tutorial Covers
By the end you will know which two dependency layers a Python signing image needs, why filename-based font detection misleads, how to pick a family at run time, and what a verification of a CJK signature does and does not prove.
Prerequisites
Python 3.6 or newer for the examples (the wheel itself supports 3.5 to 3.14); the images on this page use python:3.11-slim
groupdocs-signature-net==26.10.0, whose Linux wheel needs an x86-64 distribution with glibc 2.27 or newer
Docker, to build the two images this page compares
Understanding the Problem
Why Native Solutions Fall Short
There is no native option here. The document is a PDF, the signature is text rendered into it, and the rendering needs a font family the platform can resolve. A slim Python image is missing pieces at two levels. Without ICU the embedded .NET runtime cannot start: import succeeds, and the first call aborts the Python process with “Couldn’t find a valid ICU package”. Without the fonts a signature names, signing raises GroupDocsSignatureException with Font Times New Roman was not found. Neither message says “install this package”.
How GroupDocs.Signature Solves This
The library gives you the probe. A signature attempt against a candidate family answers, definitively, whether the platform can use it, and that answer is available before any real document depends on it. Everything else on this page is arranging that probe into a startup check.
Tutorial 1: Provision the image
What You’ll Learn
Which layers a Python signing container needs, and in what order.
Step 1: .NET dependencies
The wheel bundles its own .NET runtime, which needs the distribution’s ICU (libicu-dev, any version) and libfontconfig1. libgdiplus is needed for stamp signatures and text-as-image signatures, for barcode, QR code and image signatures with a border or transparency, for any signature on PowerPoint and image files, and for text and stamp signature previews; other text, barcode, QR code, image and digital signatures on PDF documents work without it.
No libssl1.1 and no Debian snapshot repository are needed. Versions up to 26.1 required both; 26.10 runs on the ICU and OpenSSL the distribution ships.
Step 2: fonts
PDF text and digital signatures use Times New Roman and Arial by default, so the font layer installs the Microsoft core fonts. The package lives in Debian’s contrib component and asks you to accept a license, so the layer enables the component and pre-accepts the license first - the same commands as on the System Requirements page. Metric-compatible substitutes such as fonts-liberation are not picked up in place of Times New Roman and Arial, so they do not replace this layer.
The font layer is separate on purpose, so it can be commented out to reproduce the failure:
Add a CJK font package, such as fonts-noto-cjk, to the same layer if you sign East Asian text.
Common Issues and Solutions
If the process dies on its first call with “Couldn’t find a valid ICU package”, the ICU layer is missing; do not work around it with DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1. If signing raises Font Times New Roman was not found or Font Arial was not found, it is fonts. If it raises “The type initializer for ‘Gdip’ threw an exception”, the feature you use needs libgdiplus. Keeping the layers separate is what makes that distinction quick.
Tutorial 2: Find out what the image actually has
What You’ll Learn
How to inventory fonts without depending on a graphics toolkit.
A count of zero and a count of 24 need different fixes, which is the whole reason this runs first. What the count cannot tell you is which families are available, because file names and family names differ, and the library looks fonts up by the names a font declares, never by its file name. On Windows, for example, times.ttf holds the family Times New Roman: asking for times raises Font times was not found. Even family names can surprise: the font in YuGothR.ttc resolves as Yu Gothic Regular, while Yu Gothic is not found.
Troubleshooting
fc-list : family inside the container prints the families fontconfig knows about. If fc-list is missing, the fontconfig package was not installed.
Tutorial 3: Resolve a family and sign
What You’ll Learn
How to pick a font at run time.
Step 1: Probe a single family
fromgroupdocs.signatureimportGroupDocsSignatureException,Signaturefromgroupdocs.signature.domainimportSignatureFontfromgroupdocs.signature.optionsimportTextSignOptionsdeftry_family(source_path,family_name):"""Return None when the family can be used, otherwise the reason it cannot."""try:withSignature(source_path)assignature:options=TextSignOptions("probe")options.left=10options.top=10options.width=60options.height=20font=SignatureFont()font.family_name=family_namefont.size=10options.font=fontsignature.sign("font_probe.pdf",[options])returnNoneexceptGroupDocsSignatureExceptionaserror:# The first line is the engine's message; the rest is the .NET stack tracereturnstr(error).splitlines()[0]defprobe_font_family():forfamily_namein("Times New Roman","No Such Font"):reason=try_family("sample.pdf",family_name)print(f"{family_name}: {'usable'ifreasonisNoneelsereason}")if__name__=="__main__":probe_font_family()
sample.pdf is the sample file used in this example. Click here to download it.
font.size takes an int or a float. Version 26.1 rejected an int with numeric argument expected, got 'int', and since the error appeared inside the probe, every candidate looked unusable; 26.10 accepts both.
The probe answers whether the library can find a family, not whether the family covers your script. That is why the CJK candidate list below contains CJK families only.
Step 3: Sign what resolved
fromgroupdocs.signatureimportGroupDocsSignatureException,Signaturefromgroupdocs.signature.domainimportSignatureFontfromgroupdocs.signature.optionsimportTextSignOptionsLATIN_TEXT="John Smith"CJK_TEXT="山田太郎"LATIN_CANDIDATES=["Arial","Times New Roman","DejaVu Sans","Liberation Sans"]CJK_CANDIDATES=["Noto Sans CJK JP","Noto Sans CJK SC","MS Gothic","SimSun","Microsoft YaHei","Malgun Gothic"]defbuild_text_options(text,family_name,top):options=TextSignOptions(text)options.left=100options.top=topoptions.width=200options.height=40font=SignatureFont()font.family_name=family_namefont.size=14options.font=fontreturnoptionsdeftry_family(source_path,family_name):try:withSignature(source_path)assignature:signature.sign("font_probe.pdf",[build_text_options("probe",family_name,10)])returnNoneexceptGroupDocsSignatureExceptionaserror:returnstr(error).splitlines()[0]defresolve_family(source_path,candidates):forcandidateincandidates:iftry_family(source_path,candidate)isNone:returncandidatereturnNonedefsign_with_resolved_fonts():latin_family=resolve_family("sample.pdf",LATIN_CANDIDATES)cjk_family=resolve_family("sample.pdf",CJK_CANDIDATES)print(f"Latin family: {latin_family}, CJK family: {cjk_family}")iflatin_familyisNone:print("No usable Latin font: install the Microsoft core fonts")returnwithSignature("sample.pdf")assignature:options=[build_text_options(LATIN_TEXT,latin_family,500)]ifcjk_family:options.append(build_text_options(CJK_TEXT,cjk_family,560))result=signature.sign("signed_fonts.pdf",options)print(f"Signatures added: {len(result.succeeded)}")if__name__=="__main__":sign_with_resolved_fonts()
sample.pdf is the sample file used in this example. Click here to download it.
Resolve once at startup and cache both family names. Each probe writes a real PDF, so per-request probing is waste: the Latin list costs up to four writes and the CJK list up to six, all against a one-page document. Doing that once per process is invisible; doing it per request shows up in latency graphs. Log the resolved families next to the font count; together they explain any later failure without a shell in the container.
Tutorial 4: Verify instead of assuming
What You’ll Learn
What a verification proves about the signed document, and what it cannot.
Step 1: Implementation
signed.pdf is sample.pdf signed by the previous example, with a Latin and a CJK text signature.
len(result.succeeded) above zero means a text signature with exactly that text is in the document. It does not mean the text renders: verification compares the signature’s text, not the glyphs drawn for it, so it cannot tell a correctly rendered CJK name from one drawn with a font that lacks the glyphs. To see what a reader will see, render the signed page with generate_preview and look at it.
Security Considerations
The example uses the default exact match. Without a license, verification finds no matches at all - the evaluation build reports its own evaluation text in place of your signatures, so no match type helps - and a zero count from an unlicensed run says nothing about the document. Apply a license before you rely on the check.
Do I need every font package, or just one?
For text and digital signatures that keep the default fonts, the Microsoft core fonts are the requirement: the defaults are Times New Roman and Arial, and metric-compatible substitutes are not picked up in their place. If you set SignatureFont.family_name to another installed family, that family is enough for the text it covers. Add a CJK font package only if you sign East Asian text. libfontconfig1 is not optional in any combination.
Frequently Asked Questions
Is Signature for Python actually supported on Linux?
Yes. groupdocs-signature-net 26.10 ships a manylinux_2_27_x86_64 wheel for any x86-64 distribution with glibc 2.27 or newer, with ICU and fontconfig installed. See System Requirements for the full list.
Why does every font fail when I know the fonts are installed?
Check the names first: the library finds a font by the names it declares, not by its file name, so times fails where Times New Roman works. Then check that fontconfig is installed. The 26.1 trap of an int in SignatureFont.size is gone: 26.10 accepts it.
Can I skip the .NET dependency layer on a different base image?
Only if the image already provides ICU and fontconfig. No OpenSSL 1.1 is needed: 26.10 runs on the OpenSSL the distribution ships, so the old pinned Debian snapshot is not needed either.
Summary and Next Steps
Two provisioning layers, one probe, one conditional font, one verification call. Build the fontless image once to see the failure, then the real one, and keep both around: when someone changes the base image next year, the comparison is a single build away rather than an incident.